Monday, May 9, 2011

Magnanimous Writer

Martin Fowler's essay on TolerantReader was a timely read for me, since I've been dealing with both coupling with downstream services and providing services for upstream consumers at my most recent project.

It is nice to find on the other side of a TolerantReader a MagnanimousWriter. A writer that can anticipate and forgive some of the mistakes that readers that are less tolerant will inevitably commit.

One way we tried to make our writer tolerant on our project was to abide by the following contract to the upstream consumers of our RESTful services:

1. We will version our resources thus: /v/1/widgets etc.

2. We will not make any incompatible changes to any of our existing resources.

2.a. On a GET of an existing resource, we will never provide fewer entities than we do today, although we may optionally provide extra entities over time.
2.b. On a POST to an existing resource, we will never demand more required entities than we do today, although we may allow additional optional entities over time.

3. If we ever need to modify our resources in a way that we cannot live by rule #2 above, we will always create a new version of our resources: /v/2/widgets, etc.

One way we enforced this contract upon ourselves was through a test suite that validated our schemas. Here is a sample rspec test, (over)simplified:

describe "widgets view" do
it "should match the schema" do
orders = create_array_of_widgets_from_factory
assigns[:widgets] = widgets # sets "widgets" variable in the view
render "widgets/index.xml"
rng = File.join("/dir/to/relaxng/schemas/widgets.rng")
response.body.should be_valid_against_rng(rng)
end
end

The /dir/to/relaxng/schemas directory had its SVN commit access control set up in a way that only a few people (tech lead, etc.) could commit to it. This reduced the risk where a pair of devs (erroneously) changed the element "price" to "cost" in both the code and tests, committed everything, verified all builds were green ... and cause massive heartache with our upstream consumers when the app was put into production.

Of course, it is probably impossible for a writer to be magnanimous for ever. No team can support a very large number of versions of (even slightly) different versions of the same resource. On our team, we only had one version by the time I left but we seemed to be on the cusp of "v/2/something". However, it was our collective understanding that before we ever went to "v/3/anything", we would retire "v/1/everything".