defaulting to exporting new web service methods on all web service versions makes web service mistakes dangerous

Bug #723753 reported by Leonard Richardson
6
This bug affects 1 person
Affects Status Importance Assigned to Milestone
lazr.restful
Triaged
High
Unassigned

Bug Description

Our strategy for publishing functionality through the Launchpad web service worked pretty well when Launchpad only deployed once a month. Now that we have continuous deployment it's starting to fall apart. We need to rethink it.

Let's add a method to our application (Launchpad, for the sake of example):

def foo():
    pass

Now let's publish it on the web service:

@export_as_write_operation():
def foo():
    pass

The named operation is immediately available in all versions of the web service. We did this because it was the easiest alternative. It gives users of older versions some subset of the new stuff, and we don't have to explicitly label every single thing we expose with a version name.

But it turns out we need to make a little tiny change to the named operation:

@export_operation_as('bar')
@export_as_write_operation():
def foo():
    pass

Previously, this was no big deal. We'd land the first change, realize our mistake, and fix it, all before the new version of Launchpad was deployed to production. But now the error is much more serious. Now that first change is deployed to production immediately, it shows up in version 'beta' immediately, and clients can start using it immediately. When we need to change it, we suddenly have a backwards compatibility problem. That method needs to be called 'foo' in beta and 1.0, and 'bar' in subsequent versions.

@export_operation_as('bar')
@operation_for_version('devel')
@export_as_write_operation():
@operation_for_version('beta')
def foo():
    pass

If we had limited the operation to the 'devel' version in the first place, we wouldn't have had this problem.

@export_operation_as('bar')
@export_as_write_operation():
@operation_for_version('devel')
def foo():
    pass

That's annoying, but it's the only solution I think will work.

I had an idea that lazr.restful's default behavior could be changed, so it applied annotations to the latest possible version, not the earliest. Then this code would put the named operation in 'devel' and in no other version:

@export_operation_as('bar')
@export_as_write_operation():
def foo():
    pass

The problem is that the named operation would *stay* in devel, forever, even as we released new versions of the web service. At some point this needs to go from "an operation in devel" to "an operation in 2.0." We need to lay down a marker:

@export_operation_as('bar')
@export_as_write_operation():
@operation_for_version('devel')
def foo():
    pass

So that when we release 2.0 of the web service, we can search-and-replace all the @operation_for_version('devel') to @operation_for_version('2.0'), and start fresh.

So... what action do we need to take? Unless someone can come up with a better code-based solution, we need to make a rule that all new functionality is explicitly marked as being new in devel, and enforce this rule during code reviews.

Revision history for this message
Robert Collins (lifeless) wrote :

Seems to me that we can search and replace
@export_as_write_operation():\ndef -> @export_as_write_operation()\n@operation_for_version('2.0')

With only moderately complex sed.

If we can do that, changing the default seems tractable, and in my mind (because its a default) more effective, easier on us developers.

Revision history for this message
Robert Collins (lifeless) wrote :

Alternatively, we could make operation_for_version mandatory: have the default version be 'notexported', and that would also operate as an effective safety net.

Changed in lazr.restful:
status: New → Triaged
importance: Undecided → High
summary: - Continuous deployment makes web service mistakes much more dangerous
+ defaulting to exporting new web service methods on all web service
+ versions makes web service mistakes dangerous
Revision history for this message
Leonard Richardson (leonardr) wrote :

I'm leaning towards making operation_for_version required. That will push the moment of failure to the very start of development, rather than once it's time to bump the version (or afterwards, if our sed magic doesn't catch one case or another).

Revision history for this message
Leonard Richardson (leonardr) wrote :

Fixing bug 723959 would make this task a little easier.

To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.