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

Maintainability

  • Put Code Where You Would Expect to Find It

     

    I'm going to propose a new design concept with an admittedly awkward name called the "Put Code Where You'd Expect to Find It" Principle.  It means exactly what it sounds like.  A team should strive to consistently put different types of code into separate, logical places in the structure.  Following this principle, I'm going to put security related code in a namespace with "Security" or "Authorization" or "Authentication" in its name.  Data access code is going in "Data" or "Persistence" namespaces.  Business logic is going to be placed in the appropriate Domain Model class, or an accurately named Use Case Controller (if you swing that way) or Service Layer class.  The point being, assuming a modicum of familiarity with a system, that you should be able to consistently and quickly find any piece of code by its functionality.  Think of this principle as creating a Dewey Decimal system for your code. 

    Part of the "...Where You Expect to Find It" principle is to simply create and enforce some consistency within your system.  On several of my Agile projects I've noticed "this is how we're going to do it" moments when the team settles on a pattern to follow, then follows it.  "We're going to do data access with a repository class for each parent domain class object" or "I like this pattern, let's use it for all web pages" or "let's suffix all of our Gateway classes with Gateway" are all examples.  When these patterns are defined and applied consistently, it becomes easier to find the code you're looking for.  Defining, and refining, these patterns of a system seems to lead to big jumps in developer throughput and increases a team's ability to exercise collective code ownership. 

    I think the main reason to be excited about Ruby on Rails (RoR) and its ilk is improved software maintainability through Opinionated Software.  RoR forces web developers into a bit of a straight jacket, but that's a good thing in a way.  RoR enforces a consistent source code tree layout; a consistent division of responsibilities between model, view, and controller classes; and consistency in naming conventions for just about everything.  While some of the design choices or limitations of RoR may be debatable, the advantages for maintainability are undeniable.  Any experienced RoR developer is going to be able to quickly understand the patterns of a new RoR application because the "this is the way we do it" decisions have been made as a community and baked into the framework itself.  RoR puts code where you'd expect to find it.  My colleague also suggested Jakarta Commons as another example of consistency.  I'm still a little dubious about some of the details, but I'm starting to get much more interested in Software Factories as well for the consistency that they create.

    Moving to a negative example, last year my team was extending our legacy web application and needed to find the code that governed the navigation bar at the top of each page.  We finally found the code smack dab in the middle of a huge code file called "Security.asp" in the "_ScriptLibrary" system folder created by the old Visual Interdev tool.  To make matters worse, we found three different versions of "Security.asp" scattered through the code tree.  At this point we expect to find everything and the kitchen sink in the "Security.asp" file, but it was a terrible place to dump the navigation code.  One, the name "Security.asp" doesn't imply navigation logic, and two, a system folder is an unusual place for custom code.  The application could be improved by simply moving the navigation subroutines into a separate "NavigationLibrary.asp" script in a logical folder.  Then the people who come behind us will be able to find the navigation logic when we're gone.

    The newer Integrated Development Environment tools help quite a bit with this.  I wouldn't want to live without ReSharper's CTRL-N shortcut to find any class in the solution (with wild cards too!).  Then again, finding code with ReSharper will be a whole lot easier if you employ good naming practices and put code where you would expect to find it.

  • Jeremy's Fourth Law of Test Driven Development: Keep Your Tail Short

    One of the first ongoing lesson for making Test Driven Development succeed for you is to learn how to write isolated tests with less effort.  My Fourth Law of TDD is all about recognizing the need to do testing piecewise as a vital part of formulating software designs.  One way or another, all design concepts come down to dividing the whole into granular pieces.  Keeping a short tail is yet one more design cue to help guide you where you want the design to go.

    To preclude any confusion, this post is in no way related to the Long Tail from Chris Anderson, it's only an unfortunate choice of name from me when I originally jotted down my "Laws of TDD" a couple years ago.  Before you ask about the "Fourth," here are links to the previous three "Jeremy's Laws of TDD." 

    1. Isolate the Ugly Stuff (Oct 2005)
    2. Push, Don't Pull (Mar 2006)
    3. Test Small before Testing Big (May 2006) 
    4. Keep Your Tail Short (this post, Apr 2007 - wow, did I get sidetracked)

    5-10 and the Zeroeth law will follow -- someday.

    I'm doing a presentation at DevTeach 2007 on Agile Design that has largely evolved into a code-centric discussion on performing design continuously.  A vital part of making the continuous design philosophy effective is to structure code so that it can easily accept change -- i.e. a heavy focus on the design principles that lead to Orthogonal Code.  Hand in hand with orthogonality for me is the usage of Test Driven Development (or Behavior Driven Development) to drive the design of the code in an evolutionary fashion.  TDD brings some important things to the continuous design table like a revved up feedback cycle and an almost mandatory focus on building a system through the systematic creation of small working pieces of code.

    I would go so far as to say that TDD is the single most important tool for doing design in the small (it shouldn't be your only design tool, but that's a very different post).  That being said, my own experience includes a couple projects where TDD either failed or didn't really wring out all of the advantages that it should.  Looking back at those projects with 20/20 hindsight, it's easy to make a diagnosis:  TDD was just too hard because far too much of the code had a long tail of dependencies that just couldn't be isolated.  If you wanted to test business logic, you had to pull in the database, push around configuration files, and often fire up the user interface for good measure.  TDD just flat out grinds to a halt if you have to tackle too many issues at one time. 

    Looking back at my previous writings on Test Driven Development and designing for maintainability there's an obvious underlying theme poking out from almost all of the "best practice" design concepts I've written about -- strive to do one thing, and only one thing, at a time in your code.  I want to work on one thing at a time because the human mind is finite in its ability to process multiple threads of logic.  I want to pick off some parts of the system that I understand and start coding and delivering value now instead of getting mired in "Analysis Paralysis" trying to see the whole picture.  I want to shorten the feedback cycle between coding and testing to work faster.  I want the ability to test cohesive areas of the code in isolation from other areas of the code to simplify testing.  And oh yeah, if I'm going to design and build things one thing at a time, I'd like to be able to put the pieces together at the end of the day and actually have them work together.

    If you can do one thing at a time in your code the Test Driven Development effort will go much smoother.  One of the first steps to doing one thing at a time is follow Jeremy's Fourth Law of TDD:  Keep Your Tail Short.  The best description for this general concept I've ever heard comes from Stuart Holloway - "When you pull a class off the shelf, what else is coming with it?."  Think of it this way, if I want to test my business rules, or my display logic for that matter, what other areas of the code do I have to pull as well?  By and large you want to answer that question with "not much."  Writing test code just to placate my database or web server when I'm testing business logic is purely overhead -- both in terms of the mechanics of creating the test and the intellectual overhead of understanding the test.

    I'm going to use several examples in this post that violate my Fourth Law to demonstrate why and how keeping a "short tail" is a more effective way to develop software.  These examples might seem contrived, but every single case is something I've worked on or caused.  Some of the worst examples are my recollections from my first, painful project using TDD when we did not keep our tail sufficiently short. 

    My First Foray into TDD Bombed

    My obsession with creating easily testable code dates back to my first official project using TDD.  Unfortunately the whole team was relatively green in regards to Agile engineering practices, and we made the typical mistakes that TDD newbies do by binding the UI and business logic too tightly to the infrastructure.  Specifically, I'm thinking about a workflow feature that I created.  It was a fairly simple state machine in terms of coding, but the automated testing was a different story.  I don't remember the exact details (repressed memory?), but you had to make the workflow actions in a perfectly sequential order.  There wasn't anyway just to create the state machine object to directly go to any arbitrary state.  You had to run the state transitions one at a time, and each state transition made calls to stored procedures.  I also had it set up so that the workflow read and wrote directly to the database.  The end result was the tests could only run against the database and every unit test basically had to include almost every action of the workflow.  The tests were difficult to write, almost impossible to debug, and very brittle since every unit test really depended on every other test.  My workflow logic had a long tail that reached all the way into the database schema -- and to add insult to injury we had a single shared Oracle schema for all development and testing.

    How would I do that today to drive the workflow through testing?  Actually, I think it would be pretty easy.  Move all of the persistence out into a Database Mapper, or just use NHibernate, so the core workflow logic doesn't even know the database exists.  Isolate the state machine code into a smaller class that basically only knows how to change its own state as a result of workflow state transitions, and maybe direct other services to perform actions.  I would tie the whole thing with some sort of Controller class that directs both the state machine class and the persistence service.  The Controller would be tested with mock objects in place of the persistence and the actual workflow state machine logic.  A bit like this:

        public interface INotificationService

        {

            void SendEmail(EmailMessage message);

        }

     

        public class StateMachine

        {

            private Status _status;

     

            // The real constructor could completely set up the StateMachine

            // to any point in the workflow

            public StateMachine(Status status)

            {

                _status = status;

            }

     

            public void CreateNew(INotificationService service, string assignedTo, string description)

            {

            }

     

            public void Approve()

            {

                _status = Status.Approved;

            }

     

            public void Reject(INotificationService service)

            {

            }

        }

     

        // The Controller class that just coordinates the services and

        // StateMachine objects

        public class StateMachineController

        {

            private readonly IStateMachineMapper _mapper;

     

            public StateMachineController(IStateMachineMapper mapper)

            {

                _mapper = mapper;

            }

     

            public void Approve(int issueId)

            {

                StateMachine machine = _mapper.Find(issueId);

                machine.Approve();

                _mapper.Save(machine);

            }

        }

    My goal is to make the StateMachine easy to test by making it easy to setup and completely decoupled from the database.  Most of the heavy duty logic is in StateMachine, so I would focus on isolating that logic and use StateMachineController to handle the coordination with other services.  In my analogy below, StateMachine is Aerosmith and StateMachineController is the roadie for StateMachine.

    Aerosmith and Roadies

    The one system structure that will never, ever deliver either testability or maintainability is the Big Ball of Mud.  In software design, it's imperative to define and assign the various responsibilities of your system to discrete pieces of the system.  I'm a huge fan of Responsibility Driven Design (RDD) from Rebecca Wirfs-Brock.  A key concept in Responsibility Driven Design is to think of classes in terms of stereotypes.  The stereotype of a class says a lot about the role and responsibility of a class within the greater system.  We can use the idea of class stereotypes to quickly break down a complex feature into smaller pieces and even give us some immediate ideas for minimizing the tail of dependencies between the smaller pieces.

    My own little spin on class stereotypes is what I call the "Aerosmith and Roadies" analogy.  Say you're Wozniak and you feel like putting on your own rock concert, what are the things that have to get done, and who does it?  The first responsibility is easy, Aerosmith plays the concert.  That's a great start, but it's a good bet that Aerosmith isn't going to set up the stage, plug in the instruments, get the green M&M's, and generally make sure that Aerosmith has everything it needs.  You're going to need the roadies and other people to setup the stage for Aerosmith so Aerosmith can concentrate on just playing the music.

    I'm working with financial companies now and many projects implement some form of market or trade analysis to support or make trading decisions.  For this case it's almost imperative to follow the Aerosmith and Roadies division of responsibility. 

    The actual analytical code is the obvious rock star.  The only thing the actual analytics code should do is take trade and market data that is handed to it and create knowledge and trends from that data.  The rock star analytics code doesn't know where it's data comes from, and it might not even know what's happening to the information that it creates downstream.  It's important to limit the responsibility of the analytics engine for a number of reasons.

    • The analytics code is potentially very complex.  It should be significantly easier to code if you can work on only the analytical code without worrying about the data source and downstream dependencies.
    • The analytics code has to be easy to test, and the easiest possible class (or cluster of classes) to test is one that takes in data through it's API and returns a result without calling out into anything else.  To make this happen, I must not have a long tail of dependencies on other systems, databases, and calls to services within the analytical engine.  In the automated tests, I can just set up the market and trade data in memory, run it through the analytical engine, and check the results again in memory.  It's critical to make the analytical code testable because it's making decisions about what to do with someone else's money.
    • The analytics code will change over time as the trading gurus tweak their trading algorithms.  The underlying data source and the downstream systems probably won't change at the same time.

    The analytical code has to fed it's data as an input, and something has to actually display or act on the results from the analysis code.  Borrowing terminology from Responsibility Driven Design again, fetching the market and trade data is the responsibility of some sort of Service Provider stereotype.  You would probably also create a separate set of service providers for carrying out the decisions made by the analytics engine after the analysis is complete.  In my Aerosmith concert metaphor, the market data service is like a caterer or a guitar manufacturer, and these people probably don't deal directly with Aerosmith.  You certainly can't expect Aerosmith to call up the caterer and make their orders over the phone, someone else is in the middle.  Wozniak needs some middlemen to go to the caterers and the moving trucks and set things up for Aerosmith to play.

    The middleman in the concert is the roadie.  The roadie runs around, gets the food and instruments from the truck, then delivers the food to Aerosmith.  The roadie gets the instruments out of the trucks, sets up the stage, and plugs in all the instruments on the stage.  All Aerosmith has to do is walk up to the stage and put on the show.  They don't have to be distracted by logistics.  The roadie is the Coordinator and/or Controller stereotype from Responsibility Driven Design.  It's a class that's only real responsibility is coordinating the actions of other classes.  The roadie is the glue.

    So does all of this Aerosmith/Roadie/Class Stereotype exercise do for us in designing software?

    1. Applying the concept of class stereotypes to the larger problem quickly suggests some division of responsibilities within the larger trade analysis subsystem.  In effect, we've set ourselves up to "divide and conquer" the code so we can mentally deal with fewer variables in our design at any one time.  We've reduced the bigger problem into smaller problems to limit the complexity of any one piece of code.
    2. The analytical code has a short tail and can be tested in isolation.
    3. The data provider is only that, a data provider.  We can test the data provider in isolation from the analytics. 
    4. Because the analytical code has no tight dependency on the data source, we can potentially use the analytics engine in a completely new context.  Today you're building the analytics engine to work on the trades previously created in your system on the server side.  What if tomorrow you want to provide "what if" calculations on potential trades to a user working in a completely different screen on the client side?  With a short tail of dependencies, the analytics engine is reusable.  With a long tail of dependencies on infrastructure, you're not going to be able to easily reuse the analytics functionality.

    Wait Jeremy, couldn't I just isolate the analytics engine by using mock objects or stubs for the market and trade data?  Yes, absolutely, but setting up mock expectations is also a dependency.  Anytime you find yourself doing the exact same mock object setup "preamble" in multiple unit tests it's a design cue that there might be more than one responsibility in the class under test.  Think about splitting up the class to test with less mock object setup. 

    Scott Bellware absolutely groans when I use this analogy, and that's more than enough justification to throw it in ;-)

    Can you get there from here?

    Here's an analogy for systems that are hard to test.  We live in Stamford, Connecticut.  My extended family is mostly scattered across the area where Missouri, Arkansas, and Oklahoma come together.  When we visit my relatives there simply isn't an easy way to fly from one place to the other.  We've got to detour through Charlotte or Pittsburgh or Houston to get from one place to another, not to mention the effort of driving to the airport. 

    A couple years ago I made a first pass at adding diagnostic functionality to StructureMap to create user friendly messages for common configuration errors.  I created a class hierarchy that represented an instance, all the arguments to its constructor, and its dependencies.  This diagnostic class (InstanceGraph) could only be constructed by passing in a .Net Type and a fully formed object containing the configuration of the instance.  Every time I needed to write another unit test I had to jump through hoops to create a fake Type with the correct constructor arguments, set up the configuration for the instance, then I could finally build the InstanceGraph object and look at the problems that it detected.  It just took way too long to build tests, especially since I was having to do a lot of setup that really didn't have any semantic meaning to the assertions in the unit tests.  In the end, I realized that the diagnostics could really be modeled as two distinct responsibilities:

    1. Parsing the .Net Type's and the configuration into a "design time" model
    2. Analyzing the "configuration time" model to look for missing, invalid, or inconsistent configuration

    In essence, I cut off the reflection "tail" from my diagnostic code.  I created that new model that had absolutely zero dependencies on System.Reflection and just like that I was able to write unit tests with very little friction.  I could skip the "create fake Type" test setup and go straight to the exact scenario that I wanted to test.  I'd say that I made two improvements:

    1. The unit tests required less mechanical setup work and therefore unit testing was faster
    2. The unit tests were easier to understand because there was less code noise from all of the test data setup.  That's an important quality because unit tests serve an important secondary role as low level API documentation.

    Of course I still had to build code that knew how to use Reflection on .Net Type's and the StructureMap configuration to build up the design time model, but that code is now relatively simple.

    One of the important things I've learned is that excessive test setup work is a code smell that points to a possible problem in your design.

     

    Test by Measuring What You are Trying to Test!

    A couple years ago I inherited a .Net application from another team that had just finished doing a partial rewrite from VB6 and ASP classic.  I know that team was trying to use Test Driven Development in their daily coding, but I think they hit the exact same problems with testability that I experienced on my first TDD project by letting the business code get tightly coupled to both the database and the user interface.  The application took in submitted invoices, performed copious amounts of business rules validation, and either succeeded and sent the invoice on to the downstream systems or reported the list of validation failure messages.  One of the immediate problems was the main class that built the user response was a long set of procedural code that intermingled the html creation and the business logic like this (it had been a straight port from VB6 & ASP Classic to C# by people who had never done .Net, so cut them some slack here):

        public class InvoiceScreenCreator

        {

            private readonly Invoice _invoice;

     

            public InvoiceScreenCreator(Invoice invoice)

            {

                _invoice = invoice;

            }

     

            public string CreateHTML()

            {

                string html = "<h3>" + _invoice.InvoiceId + "</h3>";

     

                // Run the complex invoice validation logic

                if (someFairlyComplexBusinessLogicDeterminationOnInvoice(_invoice))

                {

                    html = html + "<p>The invoice succeeded!</p>";

                }

                else

                {

                    foreach (string errorMessage in _invoice.ErrorMessages)

                    {

                        writeInvoiceError(errorMessage);

                    }

                }

     

                return html;

            }

        }

     

    There's plenty wrong with that general approach, but the killer to me was that there was quite a bit of complexity in the business rules and the only direct way to measure the business logic outcome was to scrape through the html created by the InvoiceScreenCreator class.  You're effectively testing business logic through side effects, and you're not able to write isolated unit tests for either the user interface or the business logic.  If something is wrong in the tests you have both UI and business logic to debug.  Testing business logic through the user interface can easily detract from the understandability of the test as user interface verbiage is intermixed with the core business logic.  It works the other way around too.  Creating the user display was nontrivial and it would have been beneficial to test the user display in isolation from the invoice validation logic.

    Oh, and needless to say for anyone who wrote or maintained ASP Classic back in the day, intertwining business logic in the middle of concatenating html together almost completely repels any effort to understand the business logic.

    Again, the solution is to separate the responsibilities for the user display and the business logic into different classes or subsystems.  The business logic runs against the submitted invoice, makes the validation determinations, and builds some sort of object that reports all of the validation information.  The user interface code could just take the completed validation report and create the display.  Make that separation and now you can test either piece in relative isolation.  In reality, the user interface and business logic are likely to change at different times.  It's worth your while to be able to change one without either affecting the other or having to understand the other piece while you work.

    Of course another lesson is that you pretty well get what you deserve when you do a straight port of dubious quality legacy code.

    Isolate the Churn

    Some elements of your codebase are going to change much more frequently than the rest of the system.  Some modules may need a very large number of testing permutations to fully cover all of the input possibilities.  In either case it's very advantageous to isolate these areas of your code from everything else.  It's smart to optimize the mechanics of testing for these modules by having a quick path to create test inputs and measure the outcome without involving any other piece of code..  Case in point, working with financial companies now I'm frequently bumping into systems that perform analysis on trade and market data to determine pricing or trading strategies.  The analysis code, especially if it's a trading strategy, is going to go through a lot of churn as the algorithms change while the backend storage for the trade and market information remains relatively unchanged.  The analysis code will inevitably require a large number of test cases to cover all the permutations of business conditions.  I think it's probably fair to say that the bottleneck in delivering the trade and market analysis is most likely the testing time and overhead.  Making that code easier to test by limiting its tail of dependencies should optimize your time to ship that code.

    Of course, if you treat the trade and market analysis code like the rock star it is, then it's already going to be isolated with a minimal tail of dependencies and you're good to go.

    Whenever you can, keep the Database on the Sidelines

    The database cares very deeply about the complete integrity of your data -- heck, that's a large part of a relational database's very purpose in life.  That very data integrity goodness is a lot of what makes a database a PITA when you're constructing automated tests.  To write an effective automated test you need to establish a combination of known inputs and expected outputs.  If you're testing against the database that means loading the database with data.  Sometimes that isn't that big of a deal, but with any level of database complexity that quickly turns into a pain because:

    • Database access in tests will make the tests run slower than tests that run completely within an AppDomain.  And yes, automated test execution time is a big deal, worthy of serious design consideration.  Enough so that I'd call it a justification by itself for decoupling business logic from the database
    • You have to make sure that dependent data is loaded first for referential integrity.  You can load a set of known reference data to help with this, but tests are generally much more comprehensible if you can see the test inputs and outputs in the same screen.
    • You have to supply some data to the database for non-nullable fields that isn't relevant to the test.  It's extra mechanical work and it's noise code in the tests.

    Think about this very realistic case.  You're building a screen to edit an existing invoice.  If the invoice has already been paid you want the screen to disable editing.  When I'm testing my presenter/controller for this scenario the only piece of information I need to set on an Invoice object is some sort of IsPaid flag.  Think about the test setup overhead of just creating an Invoice object and setting a single property versus the effort it typically takes to create an invoice in the database tables.

    You may not interpret anything in this section to mean you shouldn't use referential integrity checks in the database.  Leaving off referential integrity checks in the database is a lot like opening the kitchen door in the summer and letting all the fly's in.  It's just asking for really weird bugs in the system.  Moreover, I've often found that a lack of referential integrity makes it harder to write integration tests.  Let the database do its thing, and the business logic do its thing without each other getting in each other's way.

    Conclusion 

    Sometimes the fastest way to get code working is to write code in more, smaller pieces.  It may seem like more complexity, but I'll argue vociferously that it's better to minimize the complexity of any one part of the system rather than minimize the number of pieces in the system.  Software design and construction is all about divide and conquer.  Big classes and methods spanning multiple responsibilities and multiple concerns will never, ever be as efficient to ship as a well factored system.  Remember when you're deciding how to structure your code that code cannot ship until testing is complete.  I

     mostly presented the "Keep Your Tail Short" law in terms of testability, but it also goes a long way towards creating more opportunities for reuse and extension of the existing system.  That's crucial for doing continuous design.  Putting off technical complexity and delaying architectural commitment works best when the code is malleable, and that's enabled by minimizing the dependency tails between the classes in the code.

     

    Wait, there's even more!  I'll write a follow up soon with some more concrete examples of "Keep Your Tail Short" in regards to enterprise development.  I'm using this post, and several others over the next couple weeks, to flesh out my talking points for my "Laws of Agile Design" talk at DevTeach.  Feedback and criticism on this post would be very much appreciated.

     

  • The Don't Repeat Yourself Principle and the Wormhole Anti-Pattern

    Getting back on track with the "Maintainability" series of posts.  I'm doing this way too late at night, so the coherence might be lacking.

    Don't Repeat Yourself

    Don't Repeat Yourself (DRY) is a statement exhorting developers to avoid duplication in code.  Duplication isn't always the easiest thing to spot or even prevent.  From the Pragmatic Programmers:

    DRY says that every piece of system knowledge should have one authoritative, unambiguous representation. Every piece of knowledge in the development of something should have a single representation. A system's knowledge is far broader than just its code. It refers to database schemas, test plans, the build system, even documentation.

    Duplication is an obvious problem for maintenance, but there's a secondary meaning to the DRY Principle.  When I'm adding an all new feature to a system with new classes, database mappings and tables, new screens, web services, etc. I want to make the change with the fewest steps possible with a minimum of repetition.  I want to tell the system what I want to happen, and I want to say it only once.  More on that second meaning later.

     

    Duplication Retards Change

    For the upcoming (soon, knock on wood) StructureMap 2.0 release, I got in and added support for generic templated types.  It was nasty.  It wasn't really nasty because of Generics, it was nasty because I blundered with this innocuous looking code:

        return _pluginType.FullName;

    In some spots it was useful or necessary to identify a .Net Type with a string value and early on I fell into using the full class name as a convention.  I then promptly duplicated that simple Type.FullName logic over 70 times in the codebase.  Flash forward 3 1/2 years to the new Generics support, and I needed a way to go from a string to a type.  The obvious answer was to finally change to using assembly qualified names.  It took me about 6-8 hours total to make that one little change because of the stupid amount of duplication I had introduced with the FullName logic.

    Some other cases:

    • Multiple applications, or even subsystems of the same system, reading and writing to a shared database.  You almost inevitably end up with duplicated work to read, write, validate, and interpret the exact same data.  Think about a column in a database that represents the status of some sort of work item.  The logical entity represented by this row has different constraints and business rules depending upon what the value in that status column.  If you have more than a single piece of code that "knows" how to interpret that status value, you have duplication, and a particularly pernicious sort of duplication because it's hard to spot by looking at any one codebase.  Just as a warning, coding in a data-centric manner can open the door to a great deal of harmful duplication.  Ask yourself, if the database structure or status field changes, how many other pieces of code have to be changes? 
    • Reading and writing values from the HttpContext in ASP.Net.  This little bit of code represents a great deal of potential duplication (even if you eliminate the Magic Number Antipattern):  string something = (string) HttpContext.Current.Session["something"];  What if you want to change your state management strategy altogether?  You'll have to change every single piece of code that dipped directly into HttpContext.
    • In .Net applications, you often need to use a subclass of System.Text.Encoding when converting byte arrays to strings or vice versa.  In an application I worked with there were 67 different references to the ASCIIEncoding class.  Why do I distinctly remember this number you might ask?  Because we needed to localize the application to a Unicode encoding and I found out quickly that the change was going to lead to considerable change and effort to hunt down and make all the necessary changes.  If the character conversion code had been more centralized into some sort of helper class, that change could have been easier.

     

    Stop Duplication in its Tracks

    The worst case I've ever observed was a factory automation system.*  The system was originally built to pull upcoming factory build jobs from a MQSeries queue, go through a series of business rules, then determine the proper routing and push the new directions to other MQSeries queues.  Fine and dandy, until the day that the factory needed to start the basic process manually from a client application on the factory floor.  The developers decided to recreate the business rules portion of the existing code, rule by rule, and created a new implementation of the business rules for the new client.  I spent some time learning about both components, and it was very apparent that the new code was better structured, but trouble was right around the corner.  It's easy to guess what happened next.  Those particular business rules were volatile, but only now you had to make functionally equivalent rules changes in two very different components.  The system became harder to maintain and extend.

    The duplication was created purposely because the team felt that the original code was just too hard to reuse because the business rules and the workflow was deeply intertwined with the code that called into MQSeries.  They didn't have any test automation to catch regression bugs, and the system was hard to deploy, so modifying the existing code was quite risky.  If the original code had been much more orthogonal between business rules and the communication infrastructure, they might have been able to simply write some new glue code to interact with the existing code.  If the system had been backed up with a software ecosystem of effective build automation and comprehensive test automation coverage, the team would have been much better positioned to morph the existing code into a structure that would allow for reuse between both the automatic MQSeries mechanism and the newer manual client process.

    Part of the reason duplication creeps into code is the ease of copy/paste/modify operations to create new code.  Runaway "IDE inheritance" (copy/paste/edit coding, I couldn't find a link) can lead to a system that's very difficult to maintain.  Sometimes developers do the copy/paste/modify trick because the original code isn't quite what they need in the second case.  It definitely requires some skill and experience, but in the "not quite what I need" case, I'd much rather a developer take a little time to refactor out the common pieces first before making the second set of changes.  Refactoring is perhaps more work than copy/paste in the short term, but stamping out duplication can only help in the longer run.  Refactoring is an invaluable skill that's well worth your time.

     

    The Wormhole Anti-Pattern

    Bill Caputo wrote a good description of the Wormhole Anti-Pattern that so commonly afflicts enterprise software systems.  Roughly stated, I would define the wormhole as all of the stages a piece of data goes through to get from the database to the screen or service interface and back again.  When the wormhole gets long and involved, your development work is going to be a struggle -- hence the "Anti-Pattern" designation.

    As an almost canonical example, my first official job in software was supporting a data integration between a third party engineering application and a downstream construction application.  Between the two databases, a flat file report, two rule files, and the Tibco definitions, I counted 8 different variable names and mappings for a single piece of data along the data exchange.  The big problem was that I had to change that mapping pretty frequently -- and that meant following the path through all 8 steps.  Needless to say, that code was very difficult to troubleshoot and modify.  Of course I made all of the modifications in production to support ongoing engineering projects because there wasn't any such thing as a development environment;)  If you're a thrill seeker, nothing is more exciting than coding in the production environment while it's live.

    To apply the Wormhole Anti-Pattern to your architecture efforts, think about how many steps you would have to go through to get a new element on a screen persisted in the database.  Or to add a new feature to your application.  If the thought of jumping through a lot of Xml configuration hoops or database metadata setup or the sheer number of changes gives you pause, you may be exhibiting the Wormhole Anti-Pattern.  At that point you need to start working towards eliminating or combining some of the steps to shorten your wormhole.

    Just for comparison, we had to add some fields to a screen after it was built one week.  Here is the wormhole we have to go through on my current project.  I've had worse, but this is more than enough:

    • Element on the screen
    • Property on a Domain class
    • Property on at least one Data Transfer Object (DTO)
    • Mapping from  DTO to Domain class in the client
    • Repeat on the server side, but differently
    • Change unit tests
    • Add new field to FitNesse tests

     

    In line with the Wormhole Anti-Pattern, you might also check out the Shotgun Surgery code smell.  If you constantly make a repetitive set of changes to the same classes anytime one changes, it might be a sign that you should shorten your Wormhole by collapsing the class structure down into fewer pieces to consolidate related code into a more cohesive structure.  Your goal is to enable changes to your application to be made in fewer mechanical steps.

     

    I only want to tell you this once!

    Going back to the previous section on The Wormhole Anti-Pattern, the second, more proactive goal of the DRY Principle is to express changes in as few steps and places as possible.  My thinking in regards to the quality of a  system architecture has changed quite a bit from my brief exposure to Ruby on Rails.

    From Nico Mommaerts,

    One of the selling points of Rails is that it is built with the DRY principle in mind. DRY stands for Don't Repeat Yourself, meaning that every piece of your system is described once and only once, which should make development and maintenance a lot easier since there is no need to keep multiple parts of the code in sync. Hand in hand with DRY goes 'Convention over Configuration', another one of Rails' core philosophies. Rails uses a set of code and naming conventions that when adhered to eliminates the need for configuring every single aspect of your application. Only the extraordinary stuff needs to be configured, like legacy database schemas or other resources you don't control. Using these two philosophies, DRY and 'Convention Over Configuration', Rails lets you write less code AND more features in the same time as with a typical Java or .NET application, with easier maintenance afterwards.

    Even if you're never going to code in Ruby or build web applications, take a look at how Rails puts the various pieces together to eliminate repetition in code and configuration.  A good design allows for minimizing the amount of repetitious information.

    DRY-ing out StructureMap

    After seeing how Ruby on Rails works, it made StructureMap feel just a little shabby in some places.  Here's a specific example, one of the features in StructureMap is the ability to define configuration profiles and easily switch between them.  Typically, I like to use this feature to handle environmental differences between development, testing, and production.  There's a lot more to the functionality, but for now let's just look at the configuration needed for just a single IService today. 

    Look how ugly this is in general (couldn't get CopyAsHtml to format this for some reason), and the duplicated information between the Profile nodes, the PluginFamily nodes, the Plugin nodes, and the Instance nodes.

    <StructureMap MementoStyle='Attribute' DefaultProfile='Development'>
    <Assembly Name="SomeAssembly"/>

    <Profile Name="Production">
    <Override Type="SomeAssembly.IService" DefaultKey="Production"/>
    </Profile>

    <Profile Name="Testing">
    <Override Type="SomeAssembly.IService" DefaultKey="Testing"/>
    </Profile>

    <Profile Name="Development">
    <Override Type="SomeAssembly.IService" DefaultKey="Development"/>
    </Profile>


    <PluginFamily Type="SomeAssembly.IService" Assembly="SomeAssembly">
    <Plugin Type="SomeAssembly.ConcreteService" Assembly="SomeAssembly" ConcreteKey="Concrete"/>

    <Instance Type="Concrete" Key="Production">
    <Property Name="host" Value="PROD-SERVER"/>
    <Property Name="port" Value="5050"/>
    </Instance>

    <Instance Type="Concrete" Key="Testing">
    <Property Name="host" Value="TEST-SERVER"/>
    <Property Name="port" Value="5050"/>
    </Instance>

    <Instance Type="Concrete" Key="Development">
    <Property Name="host" Value="localhost"/>
    <Property Name="port" Value="2000"/>
    </Instance>
    </PluginFamily>

    </StructureMap>

    A major part of my work for StructureMap 2.0 has been ease of use, and that has meant eliminating the duplication and mechanical steps in configuration.  Below is the exact equivalent of the profile in StructureMap 2.0:

     

    <StructureMap MementoStyle="Attribute" DefaultProfile="Development">

     

      <Assembly Name="SomeAssembly"/>

     

      <Profile Name="Production">

        <Override Type="SomeAssembly.IService">

          <Instance PluggedType="SomeAssembly.ConcreteService,SomeAssembly" host="PROD-SERVER" port="5050"/>

        </Override>

      </Profile>

     

      <Profile Name="Testing">

        <Override Type="SomeAssembly.IService">

          <Instance PluggedType="SomeAssembly.ConcreteService,SomeAssembly" host="TEST-SERVER" port="5050"/>

        </Override>

      </Profile>

     

      <Profile Name="Development">

        <Override Type="SomeAssembly.IService">

          <Instance PluggedType="SomeAssembly.ConcreteService,SomeAssembly" host="localhost" port="2000"/>

        </Override>

      </Profile>

     

    </StructureMap>

     

    All I really did was enable a user to make all the configuration inline in the Profile node itself.  Just doing that took down the number of moving parts and centralized the semantic meaning of the profile configuration into one spot instead of being spread out throughout the Xml file.  The underlying model of StructureMap is unchanged, only the configuration code got more sophisticated to streamline the user experience.

     

     

    More than the Code

    Anytime you talk about improving the way you create software it's very hard to treat coding, design, process, and infrastructure as separate topics because they're all tightly intertwined.  You definitely want to apply the DRY Principle to your change management.  Here are a couple examples of what I mean:

    • Long lived code branches.  A temporary branch that's short lived for production support or a risky change is one thing, but a long lived branch essentially represents a whole new system.  I've seen a couple smaller product companies jeopardize their very existence by maintaining and extending customer specific branches of their system.  Hot fixes and newly demanded features often had to be implemented several different times on somewhat divergent versions of the same code.  Long lived branches need to be treated as a last resort.  If there's any possible way to arrange your system to allow for customer specific features and customizations while maintaining one version of the core code, your company will be far better off.  Microkernal designs with IoC engines (like StructureMap) can help.  Orthogonal code will help by creating plenty of seams to allow for customization.  Build and test automation makes changing code much less risky.
    • WSDL or XSD schemas for integration.  We hit this on my current project.  Our new .Net client communicates with the existing Java server platform by sending Xml messages over a stateful socket.  Quite naturally, we devolved into using XSD schema's to describe the contract of the messages.  Great, we use the XSD.exe tool in .Net to codegen DTO classes on one side, and JAXB to do the same on the Java side.  Both codebases need to have a copy of the XSD's, and that's what we did.  A copy in the .Net SVN repository and another in the Java CVS repository.  Needless to say, any change in schema from either side requires the XSD's to be copied back and forth.  This situation has caused us no small amount of pain from mismatches in the Xml definitions.  One way or another, the XSD definitions from .Net to Java need to be locked together automatically to shut down the potential discrepancies.

     

     

    The Highlander Puts it all into Perspective

    Bellware thought this was an awful analogy, so I absolutely have to use it.  If you're a big fan of the cult movie Highlander (and who isn't?), this will put it all into perspective.  The main characters in the movie were all striving to be the last one standing to win the "Prize."  As Christopher Lambert and Sean Connery intone constantly throughout the movie, "there can be only one!"

    The Don't Repeat Yourself Principle is "There can be only one!" (expression of any rule or functionality that could conceivably change)

    Unfortunately I've been on and seen a couple projects where the basic architecture just didn't allow for outwardly small changes to be made efficiently.  A nasty case of a Wormhole, plus clumsy or inefficient build processes, can make the simple addition of an extra piece of information from persistence to user interface turn into a living hell.  In the Highlander, there is a scene where the bad guy, the "Kurgan" played by Canadian character actor extraordinaire Clancy Brown, wins a sword duel and disembowels Sean Connery's character.  As the Kurgan twists the sword to inflict more pain he utters the line "it hurts, doesn't it!"  When a request for a small change comes across your desk and all you can think about is all of the painful and tedious work it will take to get that change done, that's what I call the "Kurgan Moment."

    To wrap up, the Highlander and DRY good, Kurgan and Wormhole bad.

     

     

    Appropos of nothing here, Locke is easily the best character on Lost.

  • Jay's TDD QuickStart, and the underlying problems he stumbled into

    Jay Kimble, CodeBetter's resident AJAX guru, issued a little challenge to us TDD bloggers about using Test Driven Development to develop a custom extension to the MS Ajax ScriptManager control.  In the comments, Jeff Perrin (you need to blog more Jeff) proposes a solution that's about what I would have suggested -- remove most of the functionality to a "POO" class that isn't coupled in any way to the ASP.Net runtime.

    I think the most important things to know or think about upfront before transitioning to TDD are:

    • There is a way to write an automated test for almost any piece of code, including the user interface itself (the idea that the GUI can't be tested is somewhat a myth)
    • The biggest part of driving code through the tests is thinking through *how* you're going to split up the responsibilities of the code to be able to easily write unit tests *before* you commit to writing code.  I know a lot of people blanch at the thought of doing things just for testing, but a lot of making code testable is simply a strong focus on separating responsibilities into cohesive and loosely coupled modules.  Basically, traditionally good design in general.
    • Assume that you will have to change the way you code in order to make writing the unit tests easier.  Maybe you won't, but you should still have an open mind and be prepared to challenge the way that you write code.
    • Specific to .Net, the "Visual Studio way" of creating code often detracts from testability.  More on this below.

     

    Laying down the Law

    Back to Jay's issue, let's try to apply some of Jeremy's Laws of TDD.

    1. Isolate the Ugly Stuff.  In Jay's case the ugly stuff is anything related to the ASP.Net runtime.  Following law #1, boil the "meat" of the real functionality away from the "bones" of the ASP.Net runtime.  Now, the functionality can be tested without all the overhead of a web server. 
    2. Push, Not Pull.  In Jay's control, divide the responsibilities into two pieces.  The control itself is only responsible for handling the interaction with the ASP.Net runtime and delegating to the "POO" class that actually implements the functionality.  If the "POO" class is given everything it needs to do the work, it's completely decoupled from its data source, and therefore easy to test.  Known input, expected outcome.  This is what I call the "Roadies and Aerosmith" analogy.  The "POO" class (Aerosmith) is doing all the sexy work, but the web control (the Roadie) is responsible for giving everything to the "POO" class that it needs to do its work.
    3. Test Small Before Testing Big.  Obviously, the whole thing, control and "POO" class, needs to be tested front to back in an integration test.  Before you even attempt the big bang test, build and test the constituent pieces in isolation first.  That strategy both leads to effective evolutionary design, and keeps the debugger turned off. 
    4. Avoid a Long Tail (forthcoming, but soon because I need some content for my DevTeach talks).  Jay's scenario is really too simple for this law to come into play, but think on this:  if you need to pick up any piece of logic or behavior in your system and write a test for it, how much "stuff" has to come with it?  For example, if you can't test some business logic without a database or Active Directory authentication, you're TDD efforts are going to be much more difficult than they could be.

     

    More Classes?  Yes!

    Again referring to Jay's post, one of the commenter's is concerned that the extra classes will create performance/scalability problems and I'm would also guess an unvoiced concern over extra complexity.  Two points:

    1. I vehemently oppose the attitude that more classes automatically equates to complexity.  I think a much, much more pervasive problem in the wild is classes that are bloated in size with unrelated responsibilities.  In a way, I'm personally more concerned with the complexity of any one single piece of code more than the whole.  I'll take a dozen classes with cohesive responsibilities versus a single 1000 line procedure any day of the week.
    2. I will happily stand up and shout this all damn day.  In all but the most extreme cases, the performance overhead of creating well factored code (smaller classes and methods) is trivial in the grand scheme of things.  Maintainability, of which testability is a crucial component, should never be sacrificed at the alter of performance without very good reason.  I cringe every time I read somebody say to avoid creating more objects as a performance optimization.  Sure it is, but compared to a single inefficient database query or chatty communication?

     

    The Underlying Problems of TDD in the .Net World

    Now, the very real problem that Jay's little example exposes to broad daylight:  in .Net development, and especially ASP.Net WebForms development, you often have to go out of your way to create testable code.  To steal a phrase from Jeffrey Palermo, .Net does not help you "fall into success" with good separation of concerns and testability.  In Jay's case, you can't simply write a small unit test for the control as a whole because any UserControl is tightly coupled to the ASP.Net runtime.  You can fake the HttpContext and the page event cycle, but it's nontrivial and doesn't save you much time (and no, don't ask me exactly how.  The examples I've seen relied on lots of Win32 calls and quite a bit of time with Reflector.  My advice is to go to MonoRail or wrestle WebForms into an MVP structure).  You *can* abstract away the HttpContext by surrounding it with abstracted interface wrappers, and I do recommend this, but the better question I should probably be asking, even as a fervent TDD advocate, is "why do I have to do all this extra stuff for testability?"

    One of the most popular posts I've ever written was on using a Model View Presenter architecture with ASP.Net for testability.  Half a year later I completely lost my enthusiasm for MVP with WebForms in a short morning of presentations from Prag Dave Thomas on Ruby on Rails.  What I saw in only a couple hours was a far more elegant solution for testable presentation code than MVP/WebForms can possibly provide.  At one point I even made Dave stop and repeat a sentence on how state and information was moved from controller to view because their technique eliminated most of the mechanical work we were doing to force ASPX pages into something testable. 

    One of the huge advantages of Ruby on Rails over WebForms is the inherent testability that is baked into Rails.  The way you develop pages in Rails out of the box is inherently testable, in WebForms you have to apply an extra layer of stuff that isn't built in.  Rails comes with built in extensions into the Ruby xUnit tool to stub out the equivalent of the HttpContext at will to simulate user requests, plus more extensions for common assertions for Controller to View interaction.  You can even happily test the generated web page itself without starting up a web server, and that's a huge win in terms of the rapid feedback cycles you want with TDD. 

    Rails straightjackets you into an MVC breakdown by default that separates responsibilities for easier testing.  ASP.Net is all too happy to let you throw everything into the code behinds.  I was extremely dubious about Software Factories at first, but there might be some room for a software factory for ASP.Net that leads to better practices.  We'll see.

    If Jay had been building this functionality with Ruby on Rails (which already has that functionality btw), he wouldn't have had to go out of his way one single bit because Rails is inherently testable, and that's my whole point in this section.  We want ASP.Net to have the same level of testability that Rails does.  We should be asking Microsoft to move ASP.Net in a direction that enables us to write testable code without fighting the framework.

    I don't know how much say or influence I get with this MVP thingie, but the one single thing that I would beg Microsoft for in whatever is beyond Orcas is far more consideration for testability in the .Net framework.  It's the one area in particular where I think the .Net framework needs a lot of improvement.  ScottGu, since you're amazingly all seeing, are you out there?  Can we talk about testability in ASP.Net?  Can we jump testability over new wiz bangy RAD capabilities in the priority list?  Who's with me?

    I really think the .Net community needs to reexamine and debate the merits and appropriateness of the Visual RAD approach.  A lot of what makes ASP.Net WebForms hard to test is the extra complexity that's there to support design time controls, specifically the page event cycle.  RAD is doing more damage than good because the negative impact on maintainability/testability overwhelms the "write once" advantages of RAD.  Discuss.  

     

    My Stuff on Test Driven Development

  • Lipstick on a Pig

    I'm a card carrying member of the SOA skeptics club, but I'm going to push for a dose of SOA thinking to my current client.  I do understand the potential benefits of things like BAM, BPM, and flexible orchestration if the entire enterprise were really Service Oriented, but I generally think of SOA as a kind of fool's gold for ivory tower integration architects who desperately want to play God without ever getting their hands dirty (my advice for these folks is to go get a copy of Civ IV).  For the most part, the SOA initiatives I've seen have only led to extra complexity and reduced productivity in development without ever coming close to the orchestration nirvana envisioned by the architects. 

    As I see it, the positive goal of a Service Oriented Architecture is to eliminate duplication of effort in software projects across the enterprise by reusing existing code assets and reducing the cost of changing the behavior of software to keep pace with evolving business needs.  As I've stated in other posts, it's usually easier and generally less risky to create new behavior by writing brand new code instead of breaking into existing code to make modifications.  Call me Pollyanna, but I firmly believe that those very goals can often be achieved at a lower cost by a relentless focus on creating well-factored code backed up by a healthy dollop of test and build automation (SOA certainly doesn't abrogate the need for good code behind the service points, but that's a gripe for another day).  Aw, but here's the rub:  the mass majority of the code out there in production is of dubious quality and the kind of test and build automation that enables change simply doesn't exist for most systems. 

    So back to the usefulness and appropriateness of an SOA solution, there's a phrase I've often heard or used in regards to exposing services to a nasty legacy codebase to try to extend the usefulness of said legacy code that's particularly applicable to my current project -- "Lipstick on a Pig." 

    This piggie needs some lipstick

    Here's the scenario, you're building a new client application or work process automation solution that needs to integrate or use an existing legacy server application with oodles of business logic and functionality.  What can you do?  I can think of three options off the top of my head:

    1. Write the new client as part of the legacy system.  In our case the legacy system is in a niche language, and they are committed (justifiably so IMHO) to producing all new clients in either WinForms or Swing.  This option is not on the table.
    2. Reproduce the necessary functionality in the new application.  This is the approach that we ultimately were forced to choose, but nobody is, or should be, happy with this approach.  Reproducing the existing functionality made the project take much longer, and add considerably more risk to the project, than was envisioned when the project was launched.  I say risk because we're still not exactly sure what the correct business logic should be because the original code is not entirely readable or documented.  We have also increased my client's maintenance costs considerably by duplicating functionality without retiring the existing legacy code.  Anytime this functionality needs to change, they'll have to change and test both sets of code.  Because all of the integration happens through a shared transactional database, the implicitly tight coupling between our new code and the existing code is not obvious.
    3. Find a way to use the existing legacy code in place as it is.  In effect, they could desperately use a service layer of some sort between their legacy code and newer client applications to avoid the duplication.  In terms of design, decoupling the legacy code enough to slide in a service layer is going to be difficult (I'm betting most of the functionality is in the 2-tier UI clients), but in the end I think it'll provide a heck of a lot more value to them.  Once the service layer is in place, new development work should get easier because they can just hook right up to the services with a well-defined contract.  Even better yet, the service layer should provide the ability to go to work on the service backend and start moving code over to a better factored codebase in a mainstream platform.  Putting lipstick on this pig will generate business value.

    This piggie needs to go home

    Some of my attitude on SOA originates from my days inside internal IT in a Fortune 100 manufacturing company that really is a quite logical fit for a rational SOA approach.  On a particular project, we were tasked with making significant improvements to the nasty, failure prone shipping system (as in "shuts down the factory lines when it goes down).  A senior enterprise architect was given the responsibility for determining the overall architectural changes.  His entire plan pretty well came down to replacing the existing integration points with webMethods.  The point of his strategy was to decouple the clients of the nasty legacy application from the details of the legacy application first, then make improvements to the server system at some later point.  He was perfectly honest in describing his plans as "Lipstick on a Pig."  I've always been primarily an applications guy, so I was flipping out that we were spending a lot of energy that wouldn't result in a single, observable improvement in terms of uptime, performance, functionality, or reduced support costs.  Worst was the fact that the business was told they were paying for overdue structural improvements to eliminate downtime incidents.  I even expressed an opinion that the new webMethods integration infrastructure would only add network chatter to an already chatty system and further degrade the already deficient performance.  To this day, I don't think anything far short of a full rewrite would have represented a significant improvement (and they did replace it after I was gone).  Lipstick wasn't going to help this pig at all.

     

    And no, I don't necessarily think SOA is a bad thing, but done badly or unnecessarily, SOA is a blight upon the land.

     

     

  • If it changes together, it gets built together

    The title pretty well says it all.  If changes in two or more components or subsystems can affect or break the other, you better get yourself a comprehensive automated build of some kind that exercises the integration of the two.  In particular, and my hot button issue for the day, make database changes and code changes go through the same build and configuration management.  Like it or not, a database and its application are most likely going to be strongly coupled.  If you're writing new code that requires database changes, you really, really need both sets of changes to hit the code trunk at the same time.

     

    Thank you for listening, I feel better now.

    - Roy Moore 

  • Maintainability is "ility" #1

    Occasionally, ok often, I'm gently mocked for the length of my posts.  I start with good intentions of making short, pithy Jason Yip-style posts, then think of something else I want to say and 10 pages later I manage to hit the publish button.  This is one of those that got away from me.

    I'm writing this post for a reason.  Specifically, I'm seeing a lot of harm being done on my project by focusing narrowly on performance optimizations and neglecting maintainability.  This is an ongoing discussion and debate for me at work, and I suspect it's going to continue to be an issue as long as I work in the financial world.  I think it's important to win this argument, or at least gain some concessions, so I would be very happy to hear everyone else's thoughts on this subject, especially folks that disagree with me.  In the course of this post I'm going to criticize the decisions and code of some of my teammates (and myself) on my current project.  It isn't that I think they're doing a bad job, just that there is a very real opportunity to do better.

    When you're architecting a software system, you must understand what the needs of the system really are and act accordingly.  It's important to understand the desired qualities of a system because software design often involves making compromises between opposing qualities.  For example, performance and scalability are often very much at odds -- you can generally only optimize on one or the other.  The point I'm trying to make is that you absolutely cannot focus completely on one quality of your software without considering the consequences to the other "ility" qualities of your system.

    In the end, you need to be optimizing on the qualities that genuinely create business value -- and I believe that the single most important quality for delivering business value on most projects is maintainability.  Any deviation from a more maintainable solution in favor of performance or security or scalability or whatever is dead wrong -- at least until proven otherwise.  Even if you do have stringent performance or scalability targets, I'm going to argue in this post that focusing on maintainability first will get you to those very same performance or security goals more efficiently in terms of development time.

     

    Early Performance Optimization did not Work

    To use a concrete example that I'm dealing with on my current project, the back end developers are consciously coding to minimize the number of IL instructions in an attempt to improve performance.  They're very concerned by issues like auto-boxing and the number of objects being created.  In fact, it seems to be their main judgement of code quality. 

    So our code is fast right?  Well, no, but we're working on it and making strides.  When we started integrating the client and server we found that marshalling data from the server to the client was extremely sluggish.  A little bit of profiling from one of my team members showed that we had a fairly severe bottleneck in our transport layer that totally dwarfed the run time of the rest of the system.  The IL instruction optimization in the server side code didn't particularly achieve anything.  What makes me angriest at myself is that we flirted with a more common approach to integration that I think would be more maintainable in the long run, but went with our current strategy in no small part because we thought it would be faster (sic).  I change my mind, what I'm really livid with myself for is not forcing the backend guys to benchmark the more maintainable approach first before we committed to this path*.

     

    Neglecting Maintainability is an Opportunity Cost

    The server side developers spent time making optimizations that might, or might not have, made some minor improvements in performance.  We collectively made some wide ranging architectural choices for performance that have not, in my opinion, added any value whatsoever. 

    I've got a major problem with the previous statement.  You only have a finite amount of time and resources to throw at your project.  Yeah, you can crank up your hours for short bursts, but there's always a cost for doing that.  To be truly successful, you should strive to spend these finite resources on things that add the most value.  The time spent on the server side architecture for performance bothers me a little bit, but the opportunity cost from not writing maintainable code or automated tests on the server side has been far more significant.  From Wikipedia, Opportunity Cost is "the cost of something in terms of opportunity forgone." 

    The end result?  While gaining essentially nothing in performance, we cost ourselves the opportunity to have worked more efficiently with that code in terms of both developer and project time by neglecting test automation and well factored code.    What we have is code that is genuinely hard to follow and spot errors through inspection because the methods are too long, with deeply nested if/then and looping constructs for good measure.  The existing code is much harder to change than orthogonal code backed up by unit tests would have been.  Surprise!  That server side code had to be changed in the very next iteration to add new features with additional changes looming for later iterations.  If I hadn't spent a couple of days slicing that code up with IntelliJ's automated refactoring support, we could have very easily ended up with code duplication in areas that have a high potential to change in the future.  Nothing makes extension harder than having to code the same rules in multiple places (a. More work and b. Greater chance of screwing it up).

    Even worse is poor coupling and cohesion properties that have defeated our attempts at writing isolated unit tests.  I'm all for integrated FIT style tests, but that shouldn't have to be the most granular testing that you can do on a system.  One of the lessons I've learned by dealing with so much legacy code in the last 2 1/2 years is that coding throughput is very much effected by the granularity and quickness of the feedback loop.  Writing small, granular unit tests that execute quickly leads to better productivity than the much slower feedback cycle from more coarse grained integration tests.  Having to fire up the UI to test something by hand is even slower yet.  I will very confidently claim that debugging time goes up geometrically with the coarseness of the testing, and that's significant because debugging is a major drain on developer productivity. 

    Just to beat this horse into the ground, the system we're building doesn't even have any realistic need for high performance.  The data sets are small, and the transaction complexity is fairly mild.  What we *do* need is reliability, but the stateful socket connection integration scheme we adopted in the name of performance has added complexity in the way that we deal with server connectivity.  I think a stateless connection model, while arguably slower, would have provided more value in terms of business value.  While surely improving performance, the proprietary binary formats we use for communication come with the opportunity cost of decreased interoperability, and hence a very real reduction in business opportunities.

     

    Ok smart guy, now my code isn't fast enough!

    Back to performance again.  So you concentrated on producing the correct business functionality first, with maintainability in mind, and it turns our that your architecture isn't responsive enough, or maybe can't handle the volume, or just that the user interface isn't responsive enough.   I'm not really addressing performance optimization and profiling in depth here, but take a quick read through Jeff Atwood's post Why aren't my optimizations optimizing? and you'll see that performance tuning is a tricky business.  There are too many conflicting variables to solve the problem through pure deduction alone.  Dollars to donuts, I bet you that some of the performance optimizations being done by my colleagues ended up hurting performance instead.  The point being that you almost certainly need empirical measurements to measure a range of trial solutions to arrive at better performance.  

    To drive this point home, let's say that your performance bottleneck is in the communication between physically distributed subsystems.  Forgetting for a minute about the cost of making changes to your code base, what can you do to make your system faster? 

    • Use lazy fetching techniques in fetching parent/child aggregate data structures to avoid fetching the child details when you don't need them. 
    • Use eager fetching techniques in fetching parent/child aggregate data structures to make fewer network round trips
    • Minimize the number of network round trips because that often makes the system faster by compressing the data sent over the wire or gathering data into a more coarse grained Data Transfer Object
    • Maybe the fancy compression or transformation of the data is eating up resources.  Change that to something else
    • Use more background threads
    • Eliminate thread swapping by cutting down the number of threads
    • Cache shared resources
    • Eliminate thread synchronization slowdowns caused by shared resources
    • a time to cast away stones, and a time to gather stones together; a time to embrace, and a time to refrain from embracing... (sorry, couldn't help myself)

    Wait, some of these changes contradict the other.  Which one is right?  Are you sure?  It could easily take you several attempts to find the right recipe for performance (or scalability or usability).

    You definitely need to make changes to improve performance, but those changes cannot break the functionality of the application.  Fast, buggy code isn't an improvement on slow, functional code.  If you've written maintainable code that exhibits orthogonality, you should be able to contain the changes to isolated modules without spilling into the rest of the code.  If you've built a maintainable software ecosystem of full build automation and solid test automation, you can drastically reduce the overhead of staging new code to the performance testing environment with less risk of breaking working code.  In other words, the things you do for maintainability should have a direct impact on your ability to efficiently make empirical

     

    Conclusion

    There are two general themes I wanted to explore in this post.  The first theme is just yet another cry for YAGNI.  Try not to invest time or effort into something that isn't warranted.  Make any piece of complexity earn its existence first.  A lot of this thinking is based on the assumption that it's easier to add complexity to a simple solution when it's warranted than it is to work around unnecessary complexity.  I'm also making a large assumption that you can make optimizations later if you've taken steps to flatten the change curve.  The second theme is that I think a deliberate focus on maintainable code structure and solid project infrastructure is a more reliable path to quality optimization than early optimization.  If your code and project infrastructure facilitate change you can always make adaptations to improve your other "ilities" -- assuming that you're paying attention as you work and make adaptations in a timely manner of course.

    And, by the way, maintainability is still the most important code quality.  Your system may not have to be blindingly fast, or scale like eBay, but it will change.  By all means, go learn about Big O notation and delve into the inner workings of the CLR (I'm finally reading the Jeffrey Richter book this week myself).  There's a very important point I'm trying to make for anybody engaged in building software, and that is that focusing on maintainability first is very often the most reliable means to get to exactly the other qualities that you need.  Ant if you write code that can't be maintained or changed, you're probably on a path to failure.   

     

    Wait, there's going to be more.  The next post in the "Maintainable" software series is going to be about the DRY principle and the Wormhole Antipattern.  First I'm going to give StructureMap a serious DRY'ing out for awhile, then I'm going to come back and tell you how it went.

     

    *I believe that the decision that ultimately led to our performance and tight coupling problems was based far too much on a "Sunk Cost."  More on that someday. 

  • What about Usability?

     

    Sort of a continuation of the ongoing maintainability series, it's time to look at some of the benefits.  And rant because that's just what I do.

    After publishing My Programming Manifesto (about the things *I* was thinking about at the time) post, Scott Watermasysk quite correctly stated that usability was very important and completely missing from the manifesto.  The first thing that popped into my mind upon reading Scott's post was "...it was NOT the perfect country and we