Behavior Change and State

Last post I was thinking about change and how one plans for change in the life of an application.  I became particularly interested in this after hearing from customers about the pain of versioning and how little we help them with this in the platform.  One trend that makes this even more painful is the idea that the platform can manage state for your application automatically.  The prime example of this is how Windows Workflow (WF) can automatically persist a workflow.

The vast majority of applications manage their own state.  In a typical N-Tier architecture, applications have a data layer that takes on this responsibility and provides a layer of insulation against change.  If the mean old DBA decides to change the database in some way, our friend the data layer can absorb the impact of such changes without them rippling throughout the entire application.  In fact the idea of every external interaction having a layer between it and the rest of the application is such a good idea that it is often used for services as well.  On the sending side a service agent can provide a place to contain logic that is needed for invoking an external service.  On the receiving end a service facade can be used to deal with boundary issues.

There are some principles we need to keep in mind about Behavior and State when thinking about change over time.  While not absolutes, I believe these principles to be true in general

Behavior and State are linked temporarily

You create an object, it has behavior which was built at compile time.  State on the other hand is built up at runtime.  It is either collected from a user, function calls or read from a durable store.  Eventually your object has both behavior and state that is required to fulfill it's task.  For some moment in time the two state and behavior are bound together.  Your application is designed to restore the world to this moment in time at some future point as well.

State lives longer than Behavior

The business process that your object supports outlives your object in time.  The time scale of the real world is much slower than the machine.  Instead of micro or milliseconds we operate on the scale of minutes, hours, days, weeks, months.  The state that helps us get our work done is mostly kept in durable storage so we can recall it, act on it and store it again.

Behavior Changes More Often Than State (schema)

Yes, state changes, but the schema of state changes less often than behavior.  You fix a bug, you modify a business rule, these changes occur with greater frequency than changes like adding a field to a table.

Behavior Changes often require State Migration

You changed a business rule.  Does this change affect state?  Possibly.  You might need to run the business rule again on orders placed in the last week.  You added a new field, in the database this field is now blank.  You might now need to run a process that will lookup or calculate a value for this new field in order to make the record valid.

The Place of Vulnerability

If there is one place in our application where we will feel pain around versioning it is in the place where the new behavior encounters the old state.  Whenever we have code that attempts to read state we must have a plan for what to do when we face this situation.  Increasingly the trend has been to want platform infrastructure to manage this for us automatically.  The typical way this is done in the past was to serialize the state to a stream of some kind.

While this sounds great on the surface, problems occur when the new behavior encounters this old state.  Usually at this point de-serialization fails because it doesn't know what to do with this old state.  Worse yet, if you cannot manually fix up this old state or successfully migrate it to the new state format you face the possibility of loosing data.

It seems to me that at this place of vulnerability, platform infrastructure needs to be very extensible.  This ought the place where you can modify, extend or replace platform behavior.

What do you think?

Comments (2)

  1. Evan says:

    Fundamentally, if you lower the cost of change, you lower the cost of maintainability (and TCO).  That’s a very good thing for non-trivial applications.

    "It seems to me that at this place of vulnerability, platform infrastructure needs to be very extensible.  This ought the place where you can modify, extend or replace platform behavior."

    Extensibility is good.  It allows us to tweak the framework when necessary.  A non-extensible framework requires the writers to either forsee all the possible future uses or to provide a single-context solution.

    Extensibility also comes at a cost.  Typically this tradeoff is in performance and complexity.  Generally, I prioritize performance near the bottom of the list.  It just has to be "good enough".  

    Complexity, on the other hand has very real costs, both to the producer and consumer.  The Complexity of an extensible solution requires additional investment of the framework designer and comes with a learning curve for the end user.  Depending on the level of extensibility, the learning curve may be non-trivial.

    Generally speaking, I prefer the more complex (yet extensible) solution with solid defaults.  Having solid defaults is the key to chopping off the ramp up learning curve (thus reducing cost on the customer’s side).  This also directly increases the "usability" of the framework/api.

    Although, I do want to point out that reducing cost of change in particular area of a framework or app does nothing for an application which wasn’t designed for change in the first place.

    If you’ve placed service boundaries at all your layers, classes, components, etc.  You’ve designed a system without looking at change points (what changes when I add a new field to entity X?).

    If I had to guess, there are probably many in the .NET community with "expensive to change" solutions in place.  In fact, I would probably be hard pressed to come up with another design that would cost *more* to change (unless it just had absolutely no structure whatsoever).

    But, the other *really, really* important thing to remember, is that with each change comes a new cycle of testing.  So a good framework will not only be extensible, it will be testible.  Given the amount of time the average project spends in testing, that’s another huge cost center a framework can help drive down with a little forethought.

Skip to main content