CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Jeremy D. Miller -- The Shade Tree Developer

Under the hood and working with .Net, TDD, Software Design, and Agile Stuff

February 2008 - Posts

  • Will we ever learn?

    I met a friend yesterday for lunch that works for one of my former employers.  I left this place five years ago because my division of IT had devolved into complete madness.  I've written about the organization several times, most recently in my Reversibility post with this little nugget about our process:

    At no time in the project were developers, analysts, the architect, and the testers ever actively engaged on the project at the same time.

    Basically, my then organization collapsed because we were reorganized along the "shared services" model where all the "resources" were temporarily assigned to a project for their particular phase.  The same projects that I would do today with a team of 4-6 people dedicated to the project would be done by over a dozen specialists popping in and out of the project at different times.  The fun part was that each and every person in the entire organization was spread over multiple projects at any one time (I topped out at 7-8 simultaneous projects as the "Architect").  Collaboration was basically zero.  Ownership was basically zero.  The VP who came up with this brainchild was removed and placed in charge of an ergonomics study for IT before being asked to leave (don't feel bad for her, she's now the CIO of a Fortune 500 company).

    At the same time, they tried the old centralized team of non-coding architects and the typical hilarity ensued, culminating for me the day that our chief architect tried to demand that all data access be done through web services.  You might say to yourself, isn't that an atrociously inefficient way to do data access?  Both in terms of developer efficiency and performance?  Ah, but you just don't have the proper appreciation for enterprise level architecture concerns.

    That's all water under the bridge and I've long since escaped into a more sane Agile world, but my poor friend might be going down the same path as his organization seems intent on making many of the exact same mistakes as my organization of 5 years ago.  He's just been named to the centralized architecture group and they're starting to split the different disciplines into more and more separate organizations.  The killer for me was being told that a corporate architecture group is trying to force all data access to be done through exchanging messages over a brand new ESB infrastructure.  Yee haa!
     

  • Back home, and back home

    Just to let everyone know, I'm finally back in Austin.  The long drive from Connecticut to Austin went as smoothly as it could possibly go.  I'll see all you Austinites soon at AgileATX, ADNUG, and whatnot.

    Almost as exciting to me is getting to write server side code in .Net.  I had to do all of the server side programming in Java over the last year and a half.  I like IntelliJ, but found Java programming to be higher friction than .Net, but most of the friction was caused by the client's architectures.  I'm a control freak.  Just give me complete control over all of the architecture and I'm happy.

  • 500th Post

    This is my 500th post on CodeBetter, and as Dogbert would say, it's a big, round, scary number.  I can't imagine that I'll ever make it to a 1,000, so I'll do my retrospective now.  It's been a fun ride and I'd like to think Brendan for running CodeBetter and making it what it is.  I've met a lot of people through blogging and probably gotten some opportunities that I would have missed otherwise.  

    I originally started the blog with some fuzzy idea of writing about TDD and other Agile practices to help the other .Net developers in my then company adopt and use those practices.  I don't know if any of that stuff ever helped my company, but I've gotten a lot out of writing the blog posts.  It's hard to imagine now from the constant chatter on my RSS feed and the ALT.NET groups, but three years ago Agile and .Net was a pretty rare combination. 

    I don't particularly have any kind of agenda anymore with the blog, but I still enjoy writing it.  I do need to slow down my blogging rate for the rest of the year to concentrate on other projects and that pesky little "day job" thing.  The .Net blogosphere is a much richer, more vibrant place now and doesn't seem to be stopping.  Many of my favorite bloggers started in the last 12 months, and I figure that there's probably more great bloggers just waiting to get started.

    I'm probably taking the next couple weeks off of writing because I'm finally making my move back to Austin late next week and things will be crazy at home.  I'll see you next month with the rest of "First Causes" and maybe some more "Build your own CAB" stuff. 

    I've put together a pair of "Best Of's," you know, like a country music artist that has one hit song and cuts a Greatest Hits package the next year:

    Best of the Shade Tree Developer and Best of the Shade Tree Developer Part II.

    The relevant stats are 500 posts, 3,334 comments, and 1,458 trackbacks.  My most popular post is Test Driven Development with ASP.Net and the Model View Presenter Pattern, which is a bit ironic because I've even put up a warning message on that post pushing people to look at MonoRail or the MVC framework instead of the MVP pattern for web development.  A couple months after that I read the Pragmatic Programmer's book on Ruby on Rails and I've hated that ASP.NET post ever since.  If I had to pick my favorite post, I'd go with My Programming Manifesto.

     

    Anyway, thanks to all of you who read, comment on, and link to this thing.  Here's looking to another three years or so.

  • Build your own CAB #18: The Command Executor

    EDIT:  Oops on my part.  This is effectively the Active Object pattern. 

     

    The rest of the Build your own CAB series can be found at the table of contents.

     

    There's a thread on the altdotnet list this morning about how to unit test background operations originating from a screen Presenter.  I have a strategy for that in StoryTeller that I think has worked out quite well.  Instead of creating a background worker or thread directly in a screen presenter, I run all background actions through what I call a "Command Executor."  When a presenter or a menu command needs to run some sort of asynchronous command I have that class delegate to an instance of an ICommandExecutor interface.  The interface for the ICommandExecutor in StoryTeller is shown below:

        public interface ICommandExecutor : IStartable
    {
    void Stop();
    void ExecuteCommandWithCallback(ICommand command, ICommand callback);
    void ExecuteCommand(ICommand command);
    }

    You'll see that the ICommandExecutor takes in the inevitable ICommand interface because this was written in .Net 1.1 and ported upwards later.  I think for my new project I'm going to something more like this to take advantage of lambda expressions instead of cluttering up the code with extraneous class definitions.

    public interface ICommandExecutor
    {
    void Execute(Action action);
    void ExecuteWithCallback(Action action, Action callback);
    }

    Now, why would you do this?  Using a BackgroundWorker or a background thread isn't that hard, but:

    1. The threading code is noise code that distracts the reader from the real meaning of the code.  The CommandExecutor pattern let's us change the code semantics inside the Presenter into merely a call to "run this in the background."
    2. It's potentially a lot of repetitive code to set up threads or bootstrap a BackgroundWorker.  You have to remember to do the thread synchronization every single time.  By using a Command Executor you can write the multi threaded code once and only once, including the work to synchronize threads (I use a SynchronizationContext in my CommandExecutor for the callbacks.  No need for AOP black magic whatsoever).
    3. You do NOT want any important behavioral or business logic code to be coupled to the UI machinery.  I see a lot of teams absolutely screw themselves over by embedding logic directly into the DoWork event of a BackgroundWorker, rendering that code effectively impossible to reuse or unit test.  The CommandExecutor will help push teams to separate the behavioral logic away from threading infrastructure to make that code both easier to test and reuse.
    4. The background threading is rougher in unit testing.  Not impossible, but it does add some significant overhead to the unit testing effort.

    Let's take the testing angle first.  The original problem was how to be able to unit test the background operation.  The easiest way is to simply turn the asynchronous behavior completely off by substituting in a synchronous command executor in the unit tests like this:

        public class SynchronousCommandExecutor : ICommandExecutor
    {
    public void Stop()
    {
    // no-op;
    }

    public void ExecuteCommandWithCallback(ICommand command, ICommand callback)
    {
    command.Execute();
    callback.Execute();
    }

    public void ExecuteCommand(ICommand command)
    {
    command.Execute();
    }

    public void Start()
    {
    // no-op;
    }
    }

    Now you can simply run your unit test and test the asynchronous behavior without having to setup ManualResetEvent's or other thread synchronization machinery.  In StoryTeller I usually just test that the proper ICommand message was sent to the ICommandExecutor and call it good enough.  I'll then turn around and test the ICommand.Execute() method in isolation in another unit test.

    So far, I've found this pattern pretty well eliminates all of my unit testing problems with background operations.  For integration testing one of your challenges for testing a user interface is asynchronous events.  One of the solutions I've found to this problem is to route all background operations through some sort of CommandExecutor so that the test harness has a single place to watch in order to synchronize the test script with the user interface. 

     

     

    Anyway, I banged this out fast.  Any thoughts?

  • I like this

    I don't care if you're excited about the new MVC framework or if you're one of those people that are desensitized to the pain of WebForms, the fact that this graphic was in a blog post from ScottGu has to make any ALT.NET heart go pitter-patter:

     

    All I'm excited about is the very idea that a Microsoft produced tool explicitly encourages unit testing (and acknowledges the existence of OSS tooling!). 

  • First Causes: Reversibility

    I've just started a blog series to talk about how the first causes of software development can help us make better choices and decisions.  In previous posts I talked about how we can know something and how to decide what is good.  For my very first deep dive I want to talk about Reversibility.  It's an important thing to consider because Reversibility, or the lack thereof, pretty well dictates when you have to make a decision and whether or not you can unmake that decision.  Why you care about Reversibility is summed up nicely by this quote from Martin Fowler in his seminal paper Is Design Dead?

    “If you can easily change your decisions, this means it's less important to get them right - which makes your life much simpler. ”

    Think about that.  If you can't change a decision at a later time, you've got to make that decision quicker and you've got less leeway to be wrong.  We can't control everything, but in many ways we can apply techniques and choose technologies that give us more Reversibility as a way to make decision making easier.  I see Reversibility manifesting itself in three general themes:

    1. Working and delivering incrementally.  I want to work incrementally by building a system one useful feature at a time.  There are solid economic reasons to work a project this way.  Underlying everything I'm writing about in this series is the desire to maximize the return on investment of our development efforts.  There's no return on investment whatsoever until something gets released into production.  In my company's case, we can make a release after the very first little module is complete to get an additional cashflow going.  To get that positive cashflow going I need to get the first module out the door, and it doesn't require the same complexity of infrastructure that the following modules will.  I can make the company more money by building out a simple infrastructure for the first release, even though that simple infrastructure will eventually need to become much more complex and robust.  I do want the ability to build out the more complex infrastructure later without breaking or rewriting the first module.

      There's also the issue of complexity and the limits of the human mind.  We can only consciously deal with so many variables at any one time.  It's easier to develop a system when we can eat the elephant one bite at a time by working on one feature at a time.  In order to really work one feature at a time and keep up the pace of continuous delivery, we often make simplistic solutions for some infrastructure elements.  That infrastructure might be perfectly good for the features at hand, but insufficient for later features and later system loads.  That's perfectly fine, and I'd argue that it's desirable, as long as I can reverse the simplistic solution with a more complex or robust solution later.  Not having to worry about making the infrastructure robust for later features let's me escape the analysis/paralysis trap and keep getting completed features out the door.  If I feel like I need to get some piece of infrastructure code exactly right before going any farther with features, I've got to pause and complete that infrastructure before I can continue with more features.
    2. You will make wrong choices and mistakes.  I want everybody to think hard about this.  Putting yourself in a situation where an early decision made wrongly can is expensive to be undone is going to lead to a brittle project.  It's going to be easy to fail because you're dependent upon every decision being exactly right upfront.  On the other hand, if you can make adaptations in the requirements or refactor away problems in the design later without substantial extra costs, you're going to be more resilient.  That implies some attention to finding your mistakes and problem spots in the design before it's too late, but I'll save that for a following essay on Feedback.  Working adaptively will lead to more consistent success than working only by prediction.  What would you rather base your decisions on, adapting to what is actually happening as the project progresses, or basing decisions solely on what you think is going to happen?  Adaptability is much easier with Reversibility. 
    3. Things will change.  Your current requirements are exactly right for today's business situation, and your current design elegantly satisfies those requirements, but tomorrow is going to come with unexpected surprises.  Business drivers will change.  Technologies will change.  Your understanding of the design will change.  You'll stumble into performance problems that you didn't foresee.  You'll make assumptions that will blow up in your face.  Change will happen.  With good reversibility, you'll be able to deal with change.

     

    I think it boils down to making good decisions.  Having Reversibility makes it easier for you to get decisions right.  It makes decision making easier by allowing you to make decisions without the fear that you can never undo that decision, by allowing you to wait longer in the project to make more informed decisions, and by allowing you to back out decisions that turn out to have been wrong.  In situations where you have to make irreversible decisions you're going to be more brittle because you're forced to make decisions earlier and you have less flexibility to change bad decisions.  Since I think it's impossible to be right every time, put me down for an extra helping of Reversibility.

     

    The Mini Saga of the Clustered Index

    Here's an example from my current project that brings out several of the issues around Reversibility.  My boss has a deep background in database design and development, but little in Sql Server.  He's very concerned about the choice of which column in any given database table should be the clustered index.  It's a big, important decision that potentially has a lot of impact on the performance of the system.  My boss is concerned, and he wants that decision made right now so that he can rest assured that our baseline performance is going to be good enough.  I think it's an important decision too, and that's why I think we should put that decision off for at least six months to make sure that our baseline performance is going to be good enough.  So who's right?  It basically comes down to how the two of us perceive the Reversibility issue.

    I think that decision needs to be put off until much later in the project when we have real performance numbers and usage scenarios to consider.  If we tried to design the clustered index solution right now we would just be making an educated guess that's more than likely going to be wrong anyway, so why bother?  I want to wait until the Last Responsible Moment to make that decision.  For a refresher course, as stated by Mary Poppendieck, the intent behind Last Responsible Moment is to:

    “…delay commitment until the last responsible moment, that is, the moment at which failing to make a decision eliminates an important alternative. “

    Roughly put, the Last Responsible Moment calls for you to make better informed decisions by waiting as long as possible to make a decision.  The Last Responsible Moment is the point at which you have the most information in your hands with which to make that decision.  In six months we'll have much more information about the performance characteristics of our new system and know where our database performance bottlenecks really are based on empirical performance measurements.  In six months I'll be much better able to determine which database column is the best choice for the clustered index in every table. 

    Now I have to answer the question "can I get away with waiting to make this decision?"  The Last Responsible Moment is also the last point at which you can wait to make a decision before the lack of a decision causes problems.  Using the logic of the Last Responsible Moment, I want to wait as long as possible to make well informed, and hence better, decisions.  Unfortunately, there's a limit to how long I can wait to make a decision, and this is where Reversibility comes into the picture.  If a decision is easy to reverse or retrofit into the system later, I don't need to make that decision right now.  If a decision is hard or expensive to change later, then I have to spend more upfront analysis and design time to make that decision earlier.  Decisiveness can be an admirable trait, but good decision making often requires reflection and feedback.  When I can put off a decision for later I gather more insights from the project work that can lead me to better or even simpler solutions than I might choose if I had to make the decision upfront.  Oftentimes the Reversibility of a decision is out of your control, but in other cases like my clustered index decision, I can take steps to increase my Reversibility.

    My boss's worry is partially based on how hard he found it to change the clustered index on a table using the Sql Server Management Studio GUI tool.  The admin screen promptly put up a message box telling him that he couldn't change the clustered index on a table without dropping a bunch of other indexes on other tables first.  That experience made him conclude that changing the clustered index late in the game was going to be too hard.  He was afraid that the choice of clustered index is not a reversible decision.  One way or another we're going to have the entire database schema scripted out with all of the DDL in Subversion, with a fully automated way to rebuild the database schema from scratch.  Since rebuilding the database from scratch is going to be part of the normal Continuous Integration strategy anyway, all I have to do to change the clustered index later on any table is to modify a single DDL script, check it back in, and voila! -- I've changed the clustered index.  My point being that the existence of a good configuration management strategy for the database will allow me to change the database schema at will right up to the point when we're actually ready to deploy the database to a customer.  Add in an automated regression test suite, plus the unit test coverage that falls out of TDD, and I'm much more able to make changes later.  By baking in more Reversibility with the database build and test automation strategies, I can push back the Last Responsible Moment, and make better decisions in regards to my clustered index decisions.

    Database design isn't rocket science, so why can't I take the time to do the analysis and think it through right now?  Maybe I could, but the time spent on upfront database design would be taken away from other areas where I see more immediate risk.  Performance is a risk for sure, but our biggest risks are delivering a good user experience and getting the right Domain Model in place to support the necessary behavior.  I'd rather put my time and focus on those issues now because I see those decisions being harder to reverse.  The Last Responsible Moment for the user interface machinery is coming up a lot faster than the database.  By making the reconstruction of the database schema highly automated and ruthlessly keeping persistence concerns out of my business logic and user interface code (orthogonality, but I'll get to that later), the decisions about the database structure are highly reversible.  Therefore, I can put my focus on the first complicated screen and Domain Model without worrying if I can make it work with the database or not. 

     

    Team Composition Matters

    The composition of your team and organization can have a dramatic effect on your reversibility.  If all of your decision making can be done within your team, you've got a lot more Reversibility in your decision making than you do when your decisions have to coordinated with or made by an external resource.  When you need to deal with an external specialist, you have to do more upfront planning to best take advantage of the expert while they are available to you.  The expert often adds value to your project, but the availibility of the expert decreases your scheduling flexibilityy.  I don't know if I've said it explicitly enough, but I think that a small self-contained team of full time generalists trumps a team of part time specialists every single time. 

    Earlier in this decade I worked in the manufacturing IT arm of a company that was an interesting juxtaposition of elite business moxie and putrid software development.  The company was undeniably one of the best examples of lean manufacturing in the world (the intellectual progenitor of Agile development), but its IT departments were the worst examples of bad Waterfall thinking.  Near the end of my captivity there we went to a "consulting" model (our VP was from CapGemini and Accenture).  The idea being that we would sharpen a set of very solid specialists and share those specialists across the entire portfolio of projects.  Of course, it also meant that those specialists were moved very rapidly from project to project.  Business analysts would go onto a project early, write the requirements, hand them over to an architect and move on to the next project.  The architect would take the requirements, document a system design, then (you guessed it) skedaddle on to one of the other 6-7 projects he was responsible for.  At no time in the project were developers, analysts, the architect, and the testers ever actively engaged on the project at the same time.  It was unbelievably brittle.  The whole thing crashed and burned anytime anybody made a mistake.  The developers on the team were typically not empowered to make decisions on design direction or the requirements, and it would always take some time to get the original analysts and architects back to the project to consult.  The organization wasn't that great to begin with, but it took a major step backwards because of the "consulting" model.  The business finally became angry enough that they stepped in and took over direct control of the IT program management.  I just googled the VP that inflicted that insanity upon us.  She was named CIO of a Fortune 100 company last year.

    At DevTeach Vancouver I heard Greg Young say "I can find an argument to defend everything but scope creep."  I'll pick up the torch here from Greg, because I can think of a great defense of scope creep.  Very frequently the user requirements that come in very late in the game represent some great ideas that would add a lot of value into the system.  The most popular feature of the most successful system I ever built was a near last minute user request.  Fortunately, I had access to the business folks, analysts, and testers all the way through the project and we could happily take on that user request.  If we had needed an external expert (I will NOT refer to any human being as a "resource") for any part of that feature we wouldn't have been able to coordinate with the external person fast enough to complete that feature -- and the project would have been less successful for not having done that feature.

     

    Sooner or later, you're going to hit a project that's too big for the ideal 4-6 developer sized team.  In those cases we generally have to divide the team, but how?  We could divide the team by:

    1. Horizontal layers.  In my financial development experience, that ended up being a backend Java team and a frontend .Net team.
    2. Vertical features, but maybe create a centralized team to manage shared services and framework construction.
    3. Vertical feature teams, and share the responsibility for shared infrastructure among the teams (which doesn't preclude an architecture team) 

    I choose number 3, and I'll use the concept of Reversibility to explain my answer.  Like I said above, you can most easily reverse the decisions that you own while decisions you can only make with the participation of people external to your team are harder to reverse.  In case number 1 you might be dependent upon an external team to build your web services.  Every single feature that you build is going to be dependent upon that other team.  The Last Responsible Moment is going to come relatively early as you and the backend team need to agree on a web service contract fairly early so that the backend team can start their work and your team knows what signature to code to.  In case #2, you're reliant on infrastructure code that you can't change yourself.  You can ask the central team to change the framework to better suit your needs, but that's going to involve more friction and resistance than it would in code that's completely under your control.  Again, you don't want to put yourself in a position where any mistakes are hard to reverse by putting organizational overhead in the way.

    I'm jumping ahead of myself, but choices #1 and #2 also result in less corrective Feedback by splitting the infrastructure builders from the infrastructure users.

     

     

    More examples

    • Code GenerationRob Conery just shared a story about using code generation for an admin site where the database and generated code got out of synch and eventually blew up.  If you're going to use Code Generation, I'd very strongly recommend you use active code generation instead of passive code generation.  Make the code generation part of the automated build process.  If you absolutely have to use one of those schemes where the object model is codegen'd from the database schema, set something up with the Continuous Integration build that automatically builds the database from the latest source and uses that schema to regenerate the code.
    • The Myth of Service Oriented Architecture:  I'll get flamed for this again, but I don't care.  SOA is a means, not a goal.  Used in the right circumstances it will make your business more nimble by making the business automation processes easier to change.  Great, more reversibility is good.  But think on this as you compose your service boundaries, what can you change faster, an in-process service provider completely contained within one logical system/service boundary, or the same service provider exposed externally and maintained separately by a different team.  Which is easier to change?  You do NOT have good reversibility in changing the contract of a publicly published web service contract, especially if there is more than one development team involved.  I hit this before in a Microcosm of Agile Design, but I don't think anybody picked up on the Reversibility angle of the two choices.  SOA is partially predicated on the assumption that "monolithic" applications are hard to change.  I think that good application architecture can frequently eliminate the economic advantage of building distributed SOA style solutions.  SOA is most appropriate to me in cases where rapidly changing business processes need to interact with legacy systems that aren't easy to change themselves.
    • Process and Modeling Weight:  If you invest a lot of time in creating elaborate UML models and spiffy design documentation upfront, are you really going to be as emotionally able to ditch that design when it's proven to be wrong or a better solution is discovered?  You should, because the design documentation is a sunk cost.  However, human nature is going to make that decision harder because of the emotional investment in the bad design.  One of the truisms of Agile design lore is to not give yourself any incentive to keep a bad design.  If you need to go through some sort of process vetting to make or change a decision you've also got less reversibility.  A lot of shops will use formal quality gates at various times to "lock down" designs or requirements.  That might be a good thing, unless that quality gate process makes it harder to change your design or analysis after the quality gate process is done.  I guess that what I'm trying to say is that you never want to give your teams artificial reasons not to do the right thing.
    • Project Friction:  I've been in a couple spots where configuration management practices actually made it difficult to check in code changes.  In that situation you don't want to be making coding changes any more often that you possibly have to.  More frequently, I've been in shops that had very poor configuration management processes around the database structure.  If you can't reliably synchronize database changes across different development environments, or if you have to fill out a lot of paperwork to make those changes, you'll find yourself bending over backwards to avoid making database changes -- often to the detriment of the project's quality.
    • Model First:  I do not like the concept of designing the database first then codegen'ing the object model.  I think the Agile database community has made strides in the last couple years, but Object Oriented code is much, much easier to evolve and refactor.  With refactoring tools like ReSharper and fast running automated tests, OO Domain Models are much "softer" than a database schema can ever be.  My strong preference is to use a "Persistence Ignorant" Object Relational Mapper like NHibernate and let the database largely fall out of my Domain Model (or at least make the design of the database run in parallel with the Domain Model).  I think a database first approach forces you into doing much more design upfront.  The upfront code generation is great for the initial creation, but I think those tools are balky when it comes time to make little changes.  When I want to add a property to a Domain Model class I want to go right to it in Visual Studio and add the property.  I don't want to fire up a sluggish code generation tool just to add a single property.  I also get irritated with the code generation strategies when I want to change a property name.  With ReSharper I hit SHIFT-F6 and do the Rename refactoring.  With a codegen tool I modify the code generation model, start the code generation, then compile to find all the places in my code that is now broken because the generated code changed.  No thank you.
    • VB6 COM versus .Net:  You didn't want to do much evolutionary design with COM components written in VB6.  You really wanted to lock in the public signature of the COM classes as early as possible, then warn other developers not to breathe on your IDL signature.  Anytime you changed the public signature of a COM class you risked a chance of breaking the entire COM class and rendering the newly compiled class useless from its clients.  With the advent of .Net, those problems largely went away.  As long as the binary signature of an existing method was maintained, you could happily add new things to a .Net assembly without any concern for binary compatibility.  It's no coincidence that the evolutionary practices behind Agile development first came into the Microsoft development world as we switched from Windows DNA to .Net based solutions.  Your technology choice greatly impacts your Reversibility.
    • Duplication is a Killer:  One of the best ways to increase your Reversibility is to reduce duplication in the system.  If you Don't Repeat Yourself, you can change your system much more effectively.  An example of this for me last week is the "autocomplete" behavior in our screens.  My business folks want something more sophisticated than the built in ComboBox, but for reasons of my own, I don't want to touch the equivalent control in our 3rd party suite.  Sometime when I shake free I'm actually going to write my own autocomplete functionality (sigh).  I don't think I need to do this right now because every ComboBox in our user interface is governed by an instance of the same MicroController class.  All I need to do to add the autocomplete functionality later is enhance this one class.  If I had to make that change manually to each and every ComboBox in every code behind, I'd have to stop and do that code right now.
    • Test Automation:  A solid safety net of test automation makes changing the code safer.  Any time we change an existing system we incur a risk of causing regression errors in existing code.  That fear of regression problems prevents many teams from considering improvements or changes to their system.  With a good amount of test coverage we can confidently change a system and know if and where we've broken some existing code.  Regression testing is a major cost over the lifetime of an application.  So much so that it often dictates the end of life of many systems.  I see test automation as a means to lengthen the lifecycle of a codebase.

     

    How does Reversibility relate to the other first causes?

    1. Orthogonality is a huge contributor to improving your Reversibility.  You can most easily reverse prior decisions if you only have to change that one decision.  You can't rapidly change your caching strategy if it means getting into and changing user interface and business logic code as well.  Orthogonality helps to contain changes.
    2. If we're going to take advantage of making decisions at the Last Responsible Moment, we need to gather more information about our system as we go.  Feedback helps guide us in our decisions and informs us when we need to reverse a decision.  Another quote from Martin Fowler:  "Designing for reversibility also implies a process that makes errors show up quickly."  You need to build in Feedback loops to find problems.
    3. Code readability/understandability/solubility whatever you want to call it.  In order to make a change in a system, you've got to find the place to make that change and understand the impact of your change.

     

    Summary

    Reversibility makes it easier to make good decisions.  You can partially and advantageously control the timing of your decision making by choosing tools that give you more Reversibility.  Stealing another saying from the Poppendiecks, you need to decide when to decide.  Some decisions cannot be put off and you'll have to do the best you can with these.

     

     

    There'll be more to come sometime next week.  I think I'll do Feedback next.

  • I've failed as a parent (OT)

    My son just asked me to watch cartoons with Scrappy Doo.  That annoying little twerp cartoon character was the bane of my Saturday morning cartoon days.  I still feel betrayed by Hanna Barbera for that one.

  • Container Ignorance

    There's a pretty good thread on the altdotnet board around AutoMocking Containers and the role of an IoC tool. I'm going to second something that Ayende said: 

    ...I would say that container ignorance is as important as persistence ignorance. I don't want to deal with infrastructure in my code unless I cannot avoid it.

    Ditto from me.  As the guy who's most likely been using an IoC container in .Net systems for the longest time, minimize your exposure to the IoC container.  It's infrastructure code.  The classes that do the real work in your system probably shouldn't have an reference to infrastructure.  Use the heck out of the IoC tool to put together services and object graphs by pushing dependencies into a class, but minimize the number of places where a class has to directly pull something out of the container.  Besides, if you're leaning hard on autowiring of dependencies, you shouldn't need that many calls to ObjectFactory.GetInstance().

  • AutoMocker in StructureMap 2.5

    Steve Harman asked me yesterday to show a little demo of the AutoMocking container coming soon in StructureMap 2.5.  Honestly, I was very lukewarm to the idea of an "AutoMocking" container at first because I thought it would obfuscate tests.  Everyone else was doing it, so I figured I'd give it a shot too and I built an automocker into StructureMap.  I'm not entirely thrilled with the API as it stands, so any feedback would be very welcome.

    My advice for the last four years has remained pretty consistent:  use StructureMap (or container of your choice) in unit tests very sparingly.  Just use plain Jane constructor injection inside your unit tests.  That advice aside, what you end up with is a lot of boring, repetitive code like this sample from StoryTeller that tests a class called WikiTextPresenter:

            private MockRepository _mocks;
            private IWikiTextEditor _editor;
            private ITestFormatConverter _converter;
            private WikiTextPresenter _presenter;
    
            [SetUp]
            public void SetUp()
            {
                _mocks = new MockRepository();
                _editor = _mocks.CreateMock<IWikiTextEditor>();
                _converter = _mocks.CreateMock<ITestFormatConverter>();
    
                _presenter = new WikiTextPresenter(_editor, _converter);
            }
    
    

    It's simple code, but it's code that you write over and over and over again to do interaction testing.  Instead, what if we wrote something that can

    1. Look at the constructor of the WikiTextPresenter class being tested above
    2. See that it requires an IWikiTextEditor and an ITestFormatConverter in its constructor
    3. Create a mock object for each of WikiTextPresenter's dependencies
    4. Poke all the mock objects into WikiTextPresenter for us
    5. And lastly, give us easy access to both the class under test and all the mock objects that the class under test depends on

    That in a nutshell is an automocking container.

    After seeing what the Eleutian guys were doing, I wanted something similar to their AutoMocking container just to eliminate the repetitious code of building mock objects and ramming them through a constructor function.  I built a new assembly in StructureMap called StructureMap.AutoMocking.  Inside it is a class called RhinoAutoMocker that extends the RhinoMocks MockRepository class with some StructureMap magic.  RhinoAutoMocker's responsibility in life is to connect the right mocks and/or stubs to the class under test, freeing you from the monotonous code shown up above.  Specifically, you use a generic parameter to tell the RhinoAutoMocker what the concrete class under test is.  The class itself is below.  After the break, I'll talk about usage.

        // Note that it subclasses the RhinoMocks.MockRepository class
        public class RhinoAutoMocker<TARGETCLASS> : MockRepository where TARGETCLASS : class
        {
            private readonly AutoMockedInstanceManager _manager;
            private TARGETCLASS _classUnderTest;
    
            public RhinoAutoMocker()
            {
                RhinoMocksServiceLocator locator = new RhinoMocksServiceLocator(this);
                _manager = new AutoMockedInstanceManager(locator);
            }
    
            // Replaces the inner InstanceManager in ObjectFactory with the mocked
            // InstanceManager from the auto mocking container.  This will make ObjectFactory
            // return mocks for everything.  Use cautiously!!!!!!!!!!!!!!!
            public void MockObjectFactory()
            {
                ObjectFactory.ReplaceManager(_manager);
            }
    
            // Gets the ClassUnderTest with mock objects (or stubs) pushed in
            // for all of its dependencies
            public TARGETCLASS ClassUnderTest
            {
                get
                {
                    if (_classUnderTest == null)
                    {
                        _classUnderTest = _manager.FillDependencies<TARGETCLASS>();
                    }
    
                    return _classUnderTest;
                }
            }
    
            // I find it useful from time to time to use partial mocks for the ClassUnderTest
            // Especially in Presenter testing
            public void PartialMockTheClassUnderTest()
            {
                _classUnderTest = PartialMock<TARGETCLASS>(getConstructorArgs());
            }
    
            private object[] getConstructorArgs()
            {
                ConstructorInfo ctor = Plugin.GetGreediestConstructor(typeof (TARGETCLASS));
                List<object> list = new List<object>();
                foreach (ParameterInfo parameterInfo in ctor.GetParameters())
                {
                    Type dependencyType = parameterInfo.ParameterType;
                    object dependency = _manager.CreateInstance(dependencyType);
                    list.Add(dependency);
                }
    
                return list.ToArray();
            }
    
            // Get one of the mock objects that are injected into the constructor function
            // of the ClassUnderTest
            public T Get<T>()
            {
                return _manager.CreateInstance<T>();
            }
    
            // Set the auto mocking container to use a Stub for Type T
            public void InjectStub<T>(T stub)
            {
                _manager.InjectStub<T>(stub);
            }
        }
    

    In usage it looks like this for the WikiTextPresenter class we looked at up top.  The test specifies that when ApplyChanges() is called on WikiTextPresenter, it will take the raw text from its view (IWikiTextEditor), convert that text to the internal data structure with ITestFormatConverter, and finally update some information in the View.

            [Test]
            public void ApplyChanges_with_the_automocker()
            {
                RhinoAutoMocker<WikiTextPresenter> mocks = new RhinoAutoMocker<WikiTextPresenter>();
                using (mocks.Record())
                {
                    Test test = new Test();
                    mocks.ClassUnderTest.TestPart = test;
    
                    string theWikiText = "!|SomeFixture|";
                    Expect.Call(mocks.Get<IWikiTextEditor>().WikiText).Return(theWikiText);
    
                    WikiVersion expectedVersion = new WikiVersion(0, theWikiText);
                    mocks.Get<ITestFormatConverter>().ApplyChangesFromWiki(test, theWikiText);
                    mocks.Get<IWikiTextEditor>().AddWikiVersionToList(expectedVersion);
                    mocks.Get<IWikiTextEditor>().SetWikiVersion(expectedVersion);
                }
    
                using (mocks.Playback())
                {
                    mocks.ClassUnderTest.ApplyChanges();
                }
            }
    

    RhinoAutoMocker creates the WikiTextPresenter instance under test on the first call to RhinoAutoMocker.ClassUnderTest.  The Get<T>() method on RhinoAutoMocker simply retrieves the mock object of type T that was used to construct the class under test.  Otherwise, all other usage is just the normal RhinoMocks usage.

    In case you're wondering, I chose RhinoMocks because:

    1. RhinoMocks is probably the most common mocking tool by a large margin.  It's what I use, and I ultimately wanted a tool for me;)
    2. NMock/NMock2 seems to be dead.  I finally eliminated the direct NMock support from StructureMap for the 2.5 release anyway.
    3. If you're using TypeMock you probably don't care about dependency injection and automocking anyway

    Making the RhinoAutoMocker subclass MockRepository just seemed like a good way to have all of the RhinoMocks capabilities right there at hand instead of wrapped up behind the scenes.

     

     

    If you want to use this now, you need to download the very latest code out of StructureMap's subversion repository at https://structuremap.svn.sourceforge.net/svnroot/structuremap/trunk.  Like I've said before, I'm basically done with coding on 2.5, but I'm not releasing officially until I can do a full rewrite of the website and documentation.

     

  • UI Test Automation with Project White

    I don't know many details right now, but ThoughtWorks just released something called Project White as a new toolkit for test automation of WinForms and WPF user interfaces.  It might be worth checking out.  It's released as OSS under the Apache 2 license.

    I'm happily doing automated testing of my new WinForms application, but I've purposely built that automated testing support in from the get go (I build a Window Driver into every screen and use some IoC magic).  I'll write up my solution as part of Build your own CAB soon, but I think most people will prefer something like Project White instead.

  • First Causes in Software Development: How do I decide what is good?

    Picking up from my previous post, How do I know?: Descartes' Rationalism versus Hume's Empiricism

    I had a request lately for a post on the topic of "Persistence Ignorance" (I swear I'll finish it soon Ward).  While trying to formulate that post, I realized that I needed to start with an examination of the first causes that might lead you to Persistence Ignorance.  I thought was a nearly mandatory way to start the post because Persistence Ignorance (PI) is only a means, not a goal in and of itself.  It's possible that an examination of the goals behind Persistence Ignorance might lead to a perfectly good compromise that isn't pure PI.  More interesting to me is if there is really an underlying set of qualities that we can use in a concrete manner to make rationalizations about software development techniques.  I'd like to use this post as the kickoff for a short set of essays to explore what I think those first causes are.  Just warning you now, this is going to be long.

     

    Debate is Good

    We developers are an argumentative lot.  This tool/practice/process/technique versus that one.  We're always debating the best way to build software -- and it's a good thing that we do because software development is a young profession.  Our basic laws of physics change underneath us as hardware abilities increase and multicore systems beckon.  New ideas, techniques, tools, and processes are popping up all the time.  Which to try?  What to bet my career on?  Can my tool really beat up his tool?

    Inevitably we form emotional attachments to our tools or begin to see our identity in terms of a tool.  That's unfortunate in that it blinds us to other ideas and leads to us holding retrograde postitions for a reality that might have passed us by.  We need to stop and remember why it is that we liked our current positions in the first place.  Both to better practice and understand those ideas, and also to know when to jump to something better. 

    I personally believe that debate can be healthy for us as a means to compare and contrast ideas of how software ought to be built.  In any argument or discussion however, somebody will eventually pull out this old nugget -- "I just use whatever is best for the job at hand!"  Which is a nice sentiment, but nothing but hot air without some substance behind it.  What is best?  How do we decide what is best?  Is there a constant set of basic criteria that we can use to judge software techniques through time?  I say yes.

     

    And why do you believe that?

    Frans Bouma in his exit from the ALT.NET list included this little nugget that made me sit up and pay attention:

    In general asking 'Why' wasn't answered with: "research has shown that..." but with replies which were pointing at techniques, tools and patterns, not the reasoning behind these tools, techniques and patterns. Answering Why with pointing to techniques, tools and patterns is creating a cyclic debate: the Why question is asked to understand the reasoning why some tools/techniques/patterns/practises are used. Pointing back at tools/techniques/patterns/practises isn't going to make you any wiser, as you then only learn tricks, because you can't put any argument on the table why you use pattern X, use technique Y and practise Z.

    Frans goes on to use the example of Separation of Concerns (SoC) as a concept that everybody says you should use, but nobody questions or explains.  I've called Separation of Concerns the "alpha and omega" of software design.  I obviously think it's important too, but why?  What does it give us?  To Frans' point, only by understanding those real goals can we better utilize SoC to write better software.

    It's important to understand why we espouse the things that we do, both to better understand how to apply something, and to know if we're just doing something out of Cargo Cult behavior.  What is the underlying first causes that make us think SoC is so important?  Let's take up Frans's gauntlet and start trying to answer the "why's."

    But first, before we even talk about the "why's," I want to first throw out some of...

     

    My Choices

    I've made and continue to make a certain set of choices for how I prefer to work and what I think works.  You've made your own set of choices.  Below are some of my choices and preferences.  I'm sure some of these are purely preference, but I think many of them are directly connected to my interpretation of the first causes and underlying forces that I'll discuss in the next section.

    • Evolutionary design is much more natural to me, and I strongly prefer an environment that actively supports that.
    • I'm a huge proponent of Test Driven Development and Continuous Integration practices.  I'd say that creating an automated build script is the very first development task in any project.  My tool usage is very much impacted by my ability to do TDD with a given tool.  And yes, I think TDD provides a lot of benefits above and beyond simple unit testing (more on that later).
    • I design from the middle tier or user interface first, and the database last.  Again, some of that is just preference, but there are some very real reasons to work first with your objects and let the database fall out secondly.
    • I'm all for micro code generation in forms like ReSharper's live templates, but I don't care for fullblown code generation schemes that try to build a full application from a fully formed database.  Given a choice between using a reflective approach versus code generation I'll almost always pick the reflective approach.  We can't do it in a mainstream .Net language yet, but I'll take metaprogramming over code generation as well.
    • I'm interested in the little mechanical advantages of software factories, but Software Factories as a methodology sounds like a complete non-starter to me (can you say CASE 2.0?)
    • As far as the Domain Specific Language (DSL) experimentation goes, I'm betting on textual DSL's and language oriented programming over graphical DSL tooling.  I'm much more interested in things like the Ruby acts_as_state_machine than I am in Microsoft's Workflow Foundation.
    • I'll go out of my way to avoid using designers in Visual Studio for anything but laying out elements on a screen, and I'm looking hard at dynamic layout techniques for my system.  I'm going down a path of using my own fluent interface DSL to express data bindings and screen behavior in WinForms because I think it's faster than using the design time support in WinForms.
    • I favor explicit coding over magic.  I vary rarely use custom events in my code because I think they obfuscate the code and make it harder to understand.  I prefer explicit observer classes as need be.  Call me strange on that one though because I'm close to a minority of one.
    • I prefer to understand code by examining and reading the code.  I'm not a big believer in external documentation (mostly because I've never found other people's documentation to be very useful and I know that the documents that I labored diligently over in the past were never read by anyone but me).  Code readability is paramount to me (but of course we all have our different ideas of what that means).
    • I generally think that most (but not all) comments in the code are an apology for bad design rather than something that is helpful.  The xml documentation embedded in code makes code harder to read.
    • I like clean separation of concerns in my architectures.  I'm very iffy about Active Record approaches in static typed languages.  I intensely dislike architectures like CSLA.Net that ignore Separation of Concerns -- even though CSLA.Net obviously has plenty of fans.  I think orthogonality in a software design is a paramount concern that cannot be sacrificed for short term productivity gains. 
    • I absolutely detest intrusive frameworks, especially if they involve a lot of inheritance chains. 

    Ok, there's what *I* like.  Now, let's see if I can connect some of these choices to what I think the underlying forces of software development really are.  And of course, if I can't make the connection, I need to reconsider these preferences;-)

     

    First Causes and Values

    Underlying everything we choose is a set of drivers.  At the very lowest level is the simple goal:  "provide as much customer value as possible as efficiently as possible."  You might say that your first cause is to just get it done.  Great, but what does "done" really mean?  I'm going to take the XP line here, "Done, done, done" means that the code can ship.  The functionality is complete, it's tested well enough, and the customer has approved the functionality.  It's not good enough just to write code, we also have to have ways to make sure the code we've written is correct and that we've written the correct functionality in the first place.

    Most of the effort in software development is deciding what to do.  The construction of software is nothing but typing.  I've seen it argued that the most important, and time consuming, activity in all of software development is learning.  Learning what the customer wants, what they need, how our architecture performs, and learning that our initial design wasn't that great when we actually tried to implement it.  I don't think we can really know everything upfront, so if we're really setting off on a project without knowing the exact path, we better make regular course corrections with plenty of...

    Feedback - I honestly think this is the single most important "First Cause."  My experience is that productivity is most heavily impacted by our ability to quickly apply some sort of validation to anything we do.  I've just written a requirement / designed a screen layout / coded a requirement / sketched a UML diagram, is it right / valuable / usable / not really what the user intended?  I strongly think that a team's or individual's tools and practices should be chosen with a very strong regard for the quality and quantity of feedback that those tools and practices provide.

     

     

    A couple weeks back I was in some discussions about the future Composite WPF guidance.  One of the participants made the remark that we should be careful not to sacrifice productivity for the sake of testability.  I think that's a false dichotomy.  When we talk about getting to "done" we need to be careful to consider everything that it takes to get to done, and that includes the effort that we put into removing defects from the code.  We need to optimize the complete lifecycle of the code, not just the initial production of the code.  Being fast to code and hell to debug when anything is wrong is a net loss.  So let's add...

    Testability - The simple ability to test and verify a system bitwise.  How easy and quick is it to validate parts of the application work?  I feel like this is a specific case of Feedback, but merits it's own treatment.  I've written about this quite a bit in the past, but I want to use the essay on this topic to challenge my own views a little bit and see how things fall out in the post-TypeMock world.

     

    Software is complex, and the overall system may have hundreds and even thousands of variables.  The human mind can only hold so many variables at one time.  What we need to do is to be able to pick off logical parts of the system and work them in isolation.  We need to eat the elephant one bite at a time.  That leads to iterative development processes for the team, and in the code we need...

    Orthogonality - Simply put, the ability to divide and conquer by being able to work on a single concern of the whole system at a time.  Every major design technique that I'm aware of exists to provide a heuristic to take an amorphous blob of requirements and decompose it into a series of approachable coding tasks.  The ability to work on one thing at a time is crucial for productivity.  Designs that allow us to turn a complex system into a series of simple tasks are good.  Designs that organize the code to make the assignment of responsibilities predictable are good.  Designs that throw all the code into a giant DoWork() method aren't helpful.

     

    I'd also extend "I just want to get it done" to being able to sustain ongoing efforts in the same code later.  Most systems I've worked on are continuously maintained and extended until they just become too expensive to maintain and extend.  Clearly, there's a huge economic value in these systems to providing for sustainable development.  Along the way, I've observed (empiracism) a very large disparity between my productivity in one codebase versus another codebase.  For purely economic reasons, I definitely want to build in the ability to change a system over time.  If I'm going to get in and change a system over time, I want a couple of qualities in my system to make that change safe.  Feedback loops, Testability, and Orthogonality all play major parts in making it safe and economical to change an existing system, but there's another major factor in a software design's ability to change over time:

    Reversibility - The best example I can give for Reversibility is watching my 4 year old son paint with water soluble paint across the table from me as I write this.  I'd be a lot more worried about what he's doing if I didn't know that I can easily wash it all off later.  The same thing applies in software design.  If I know I can reverse a design decision later without serious consequences, I don't have to be that worried about getting it perfect now.  That's a hugely liberating feeling.  If I can build the system's persistence scheme simple, and fit in caching later when the volumes demand it, I can build the system right now for the initial release without having to make those decisions about caching upfront.  When we have Reversibility, continuous design is possible and analysis/paralysis problems can be put to bed.  My design choices are directly informed by my wish for better Reversibility. 

     

    Oh yeah, and people stuff too.  I wrote ad nauseum about teams and communication and collaboration last year because my client was self destructive in this regard.  That's all available at:  On Software Teams

     

    The Series

    If I've tallied things up correctly, I'd like to write some deep dives on:

    • Feedback
    • Orthogonality (mostly links though)
    • Testability
    • Reversibility - Its impact and importance for doing design.  What design choices give us more or less reversibility.  I also want to look at how external factors can improve or diminish reversibility
    • Can you "see" the flow of the code?  Solubility, readability, whatever you call this quality of code.  Can I tune into the "signal" in the code that I care about without getting bogged down by unrelated "noise?"  I think I'm going to say that this is a specific manifestation of Orthogonality.
    • Why and How TDD Works - First and foremost, TDD is a design technique, and I'm going to treat it as such.  TDD is more than unit testing.  TDD encourages Orthogonality and provides a fast Feedback loop. 
    • Persistence Ignorance (PI) - Just applying the first causes to the problem of persistence and seeing where PI applies.  It's just a means for better Orthogonality and Testability. 

     

    The Fly in the Ointment

    There's at least a couple problems with my arguments in this post that I'm happy to acknowledge:

    1. Not every system or project is the same.  As I've said before, I strongly favor using a Domain Model architecture backed by an Object Relational Mapper that supports "Persistence Ignorance."  I also said that I don't care for the Linq to Sql or NetTiers type of approach to system building, but there are a lot of systems where the entire goal is to attach a user interface to a database in the fastest way possible.  I think data-centric approaches are perfectly applicable in those cases, even though I would never touch them in any of the systems I build that tend to be very rich in behavior and business logic.  All I mean to say is that our choices of "what is good" are very much influenced by the work that we do.  If you work primarily on CRUD or reporting applications that don't change much throughout their lifecycle, then you won't care about the same things that I do.  Then again, you're going to get into serious trouble if you take those data-centric techniques and try to apply them to a system with rich domain logic.
    2. Opportunity Cost.  Just because your favored solution "works," doesn't mean that there isn't a better way out there that would give you better results.  If that's the case, you're actually losing time and money by using your current techniques, technologies, or processes.  The first example that comes to my mind is a talk I sat in last spring.  The high priced architect speaking was demonstrating his application framework.  The framework required you to write a *lot* of little factory methods and classes by hand.  I asked him why he didn't just use an IoC tool to avoid all of that monotonous coding.  His response to me was that his way has always "worked" for him.  Yes it worked, but I could have done the same thing with less code and mechanical effort.  I think that TDD works for me, but maybe there's something totally different I could be doing instead or more likely, a better way to do TDD.  Maybe Design by Contract could replace some parts of TDD for me at some point (I still see DbC as a small subset of TDD, not the other way around). 

     

    I can make both an empirical statement that TDD works, and I can provide the rationale for why it works, but there's always the possibility that something better is out there.  Even if you "know" what's right, you better keep a look out for better things.

  • Some Lessons Learned

    1. When you have a cheap knockoff DVR, make sure that you record the program *after* the game just in case it goes long.  I just missed the last half of the 4th quarter of the Super Bowl.  Grr.
    2. When you're creating test language grammars for acceptance testing, you absolutely must do it with the non-developers.   Oddly enough, it helps to write what's supposed to be a business facing test language with the involvement of the business folks. 
  • How do I know?: Descartes' Rationalism versus Hume's Empiricism

    I really don't have time for this, but I'm going to do it anyway.  I'm starting a new blog post series that's roughly "How do I know what the best way to write software is?"  Before I get started on it, I wanted to talk just a little bit about how we know something. 

     

    I took a couple classes in college on Philosophy, and one particular set of lectures stands out to me all the way to this day.  We spent a long time talking about epistemology, the (not so) simple subject of how we obtain knowledge.  The first approach is the Rationalism of Rene Descartes.  In this view, we "know" something only if we can know it to be infallibly true.  To a rationalist, "knowing" Separation of Concerns is a good practice means that you understand exactly why and how Separation of Concerns leads to better results. 

    The other view promoted by David Hume and others is Empiricism.  As the name implies, empiricists "know" something to be true if it has been observed to be true.  To an empiricist, the only way to know that Separation of Concerns is valuable is to have made and recorded observations that show improved quality and sustained productivity.   

    The unfortunate trap is that we can be wrong in a variety of ways.  As an empiricist would say, our reasoning can be false and lead us to erroneous conclusions about what we should do.  Descartes himself fell victim to this in his own postulated theory of light, which isn't consistent with modern physics.  The Rationalists will tell us that we can't trust our senses.  So even if hard numbers and measurable observations can lead us to "know" something, what does a study or even our own experience really tell us?  We can easily make false deductions from our observations or fail to make the observations that are right in front of us. 

    There was recently a study published about the effectiveness of Test Driven Development debated by both Phil Haack and Jacob Proffitt that seemed to suggest that using TDD leads to higher quality software -- or maybe it doesn't do anything of the sort.  I'm not taking the study seriously either way because:

    1. My personal experience has led me to believe that TDD is effective and leads to better structural qualities of code and higher efficiency.  Generally, I've learned how to effectively apply TDD and I feel like I understand why and how it works.
    2. The study was performed on college students.  TDD has a nontrivial learning curve, and punishes developers who try to write code with tight coupling or lack of cohesion.  I don't think I could have effectively utilized TDD as a college student.  Heck, I struggled on my first experience with TDD and at the time I had 3+ years of lead experience under my belt.  Now, if I'd had a coach with TDD experience, maybe that would have been different.

    Agile practices are criticized, and maybe rightly so, because of a lack of empirical numbers to prove the claimed benefits of those practices.  I'd certainly worry if I saw multiple studies showing that Agile practices were creating worse results than traditional methods.  I won't dispute the value of empirical data and methods like Six Sigma.  All the same though, I think that rationalism is in some ways more important to us in software development because I think it's just too hard to use empirical data alone to make decisions in software development.  We simply don't have enough history behind us to have accurate empirical proof for just about anything.  If nothing else, we need to apply a lot of analysis to any empirical study we do have to understand the causes for the numbers.

    One of the things that keeps software development from being a straightforward science is that our "laws of physics" are changing rapidly.  A couple years ago I read some older authors questioning the effectiveness of TDD by quoting several studies performed in the late 70's and 80's about the ineffectiveness of COBOL developers doing unit testing.  The reality is very different for .Net or Java development today than it was for COBOL development.  TDD is viable with modern programming languages where the atomic units of code are much smaller for isolated unit tests and the compilers are fast enough to enable the TDD feedback loop.  I really don't think those unit test studies on COBOL are all that relevant to me.

    It's important to us as software developers to consider why and how a practice works because that examination of the underlying forces will help us to better apply that practice.  Most of the benefits of the tools and practices that we use aren't automatically delivered to us on our initial implementation.  The benefits are largely determined by the manner in which we apply practices.  I think Agile practices are advantageous, but yet I've seen instances where they failed my team and I.  I feel like I can rationalize why the successes worked and the failures failed:  feedback cycles and reacting to that feedback, orthogonal architectures, clean code, good collaboration between team members, seamless handovers, and an environment that actively supported an adaptive approach.  Forgetting whatever you happen to call your approach or what your approach actually is, I think that being mindful of the first causes will point you in the direction of success.  In the next post I'll kick off my discussion of just what I think those first causes are.

     

     

    The Afterward

    When I worked at ThoughtWorks, we had an Analyst with a background as a professor of philosophy.  I had a conversation with another developer about Descartes vs. Hume one day in the team room as we were pairing.  From the look on my analyst's face, I don't think I would have passed his philosophy class;-)  I'm going to email the link to him just so I can imagine how much he'd cringe over this post.  There's a good discussion on Rationalism versus Empiricism from the Stanford Encyclopedia of Philosophy that explains the differences in much more detail.  Please feel free to correct my interpretation of Descartes and Hume in the comments, and I promise never to dip this shamelessly into pseudo-intellectualism ever again. 

  • ReSharper 4 in 2 weeks (and random geekiness)

    From the source

    It's only been a couple weeks of clumsiness with VS2008.  Nowhere as bad as these waits:

    • The Lost season premiere last night
    • Return of the Jedi as a 9 year old
    • The neverending wait for A Memory of Light
    • George R. R. Martin's next book (didn't he say he was basically almost done when he published the last one?)
    • Stephen King to write another Dark Tower book
    • Waiting for football season to start after the end of the NCAA tournament (not a baseball fan)
    • Last day of school
    • IronRuby to be usable for production code

     

     

     

  • Access to the StoryTeller source code

    I've had a couple people over the last couple weeks ask about how to get access to the StoryTeller source code.  All you need to do is register for a user account on Tigris and use those credentials when you're challenged by SVN for credentials. 

    The source code itself is at http://storyteller.tigris.org/svn/storyteller/trunk


    And yes, I am still actively developing it, it just got shelved for other projects.  I'm using it for acceptance testing in my new job and I'll release after the (many, many) kinks and annoyances are ironed out.  Ah, the joys of dogfooding.
     

More Posts