My opinion is that any time we face an incompatible change, the factors we
should consider are:

- How significant will the benefit be?  (In this case, it was huge
  because the byte[] were inherently broken.)
- How many users do we expect it to affect (e.g., Java vs. OCaml
  changes).
- How difficult is the upgrade path?  (Changing the code to create a
  server is relatively simple.  Changing all code that works with
  generated structures is harder.  Changing the wire protocol is
  prohibitive.)
- What is the effect if client code is not updated?  (In C++ or Java,
  you probably get a nice, safe compile error.  In Python, you might get
  difficult-to-explain behavior after your app has been running for a
  while.)

It's possible that I've left something off the list, but these are the
ones that come to mind.

This might be a controversial opinion, but I place no weight in version
numbers at all.  You'll notice that I intentionally left "whether we
have released version 1.0" off of my list.  Of course, others might
place weight on a 1.0 release, so it could indirectly affect my second
point.

>> At some point in the project's lifecycle, I think that Thrift should shift
>> to a "don't break existing code unless absolutely, positively necessary for
>> performance/security/etc."
>> But perhaps there's an explicit policy in place now that "Since we're only
>> at version 0.4, all bets are off and any marginal change justifies breaking
>> existing code. We'll stabilize at 1.0."
That's a reasonable position, but as both a developer and user of
Thrift, I personally would prefer not to do that.  When upgrade paths
are easy and upgrade errors are easily detected, I would *never* want to
be prevented from making a beneficial change because it wasn't
"absolutely, positively necessary".

If this means never releasing version 1.0, that's fine with me.  I
recently learned that Hadoop (an incredibly successful project) is still
hanging out at 0.22.  I'd be fine with Thrift doing the same.

If we do decide to release a version 1.0 and refrain from making
unnecessary breaking changes even if they are very beneficial and easy
to upgrade to, I would want to immediately create a 2.0 branch that
would accept more aggressive changes.

--David

On 08/24/2010 12:12 PM, Bryan Duxbury wrote:
> In general, I don't aim to break things. I hate updating my code as much as
> the next guy. However, we are still pre-1.0, which means if we're going to
> break code, now is the time to do it. In fact, we should get as much of it
> out of the way as we can before our user base and feature set becomes so
> entrenched that we're permanently committed to given interfaces.
> 
> That said, I reject your premise that we "break compatibility for virtually
> no benefit". In fact, this particular change was effected specifically to
> give us a substantial performance benefit for structs with many binary
> fields. (It also makes a lot of operations easier, including map keys and
> set elements, comparison and equality, etc.)
> 
> Finally, I would *gladly* accept a patch to the Java generator that
> generates accessors with the old-style signatures that wrap the new style
> ones. There's no reason for us not to have a 0.4.1 if it would ease folks'
> lives.
> 
> -Bryan
> 
> On Tue, Aug 24, 2010 at 11:44 AM, Dave Engberg <[email protected]>wrote:
> 
>>  [ Caveat:  I should obviously spend more time keeping up with the daily
>> email traffic to weigh in on proposed changes before full releases, but you
>> know how it goes at a start-up.  Anyhoo ... ]
>>
>> I'm starting to go through the process of updating our API to Thrift 0.4,
>> which hits around a dozen apps that we maintain (clients and server) and
>> around a hundred implemented by third party developers.
>>
>> I tend to see changes in Thrift releases that break the external interfaces
>> of generated code.  In some cases, this may be based on a heavy conscious
>> decision, but other changes seem to casually break compatibility for
>> virtually no benefit.
>>
>> For example, in 0.4.0, the Java generated interfaces for 'binary' fields
>> changed everything from the built-in 'byte[]' Java type to the
>> java.nio.ByteBuffer class.  This means that all relevant method signatures
>> and fields were changed, and all instance setters and getters went from:
>>  public byte[] getImage() ...
>>  public void setImage(byte[] image) ...
>> to:
>>  public ByteBuffer getImage()
>>  public void setImage(ByteBuffer image)
>>
>> That means that we'll need to change a hundreds of lines of code from:
>>   foo.setImage(bytes);
>> to:
>>  foo.setImage(ByteBuffer.wrap(bytes));
>> and from:
>>  byte[] bar = foo.getImage();
>> to:
>>  byte[] bar = new byte[foo.getImage().capacity()];
>>  foo.getImage().get(bar, 0, bar.length);
>>
>> (This particular compatibility change seems particularly gratuitous since
>> it actually increases the cost and overhead of constructing objects and
>> marshaling data while replacing a core Java type with an odd dependency on a
>> low-level Java IO package that's not really designed for this type of
>> thing.)
>>
>> So my meta-question is:  what's the Thrift project's philosophy about
>> maintaining interface consistency and backward compatibility between
>> releases?
>>
>> At some point in the project's lifecycle, I think that Thrift should shift
>> to a "don't break existing code unless absolutely, positively necessary for
>> performance/security/etc."
>> But perhaps there's an explicit policy in place now that "Since we're only
>> at version 0.4, all bets are off and any marginal change justifies breaking
>> existing code. We'll stabilize at 1.0."
>>
>>
> 

Reply via email to