Shawn Hargreaves Blog
If you've been paying attention to my recent posts, you probably noticed I have been thinking about ways we could improve the XNA Framework API. Which begs the question: how much is it possible to clean up an API while maintaining backward compatibility?
Backward compatibility is a terrifying topic for API designers. You just can't win.
If we make breaking changes, people are annoyed because their old code no longer works. And worse, this resets the supporting user community. Books, articles, samples, and engine code must be updated to the new version. This might be ok if it only happens every couple of years, but there is a limit to how often we can expect our community to deal with such a disruption.
But if we never make breaking changes, our product stagnates. Every design mistake and not fully thought out feature is forever cast in stone. Every random quirk of obsolete platforms must be inherited by all future platforms until the end of time. That isn't good for anyone.
In the past, XNA Game Studio has attempted a middle road. We made changes, knowing this could break some games, and required a recompile to move existing projects over to new framework versions, but we provided an upgrade wizard to help with this process, and tried to minimize API changes to make the upgrade as painless as possible. Multiple framework versions can be installed side by side, so old games continue to use the old framework while newer games use the new version. This allows us to make minor tweaks that we do not expect to break most games, knowing that if this causes problems for a few people, they will have a chance to fix their code while upgrading their projects (or of course they can choose to stick with the older version). We occasionally made bigger breaking changes in areas where we felt the benefit outweighed the cost, but every time we do this we see pain when customers try to use older tutorials with newer framework versions, then get confused because things have changed out from under them.
This compromise has worked ok for us so far, but it’s a confusing message: most stuff still works the same, except for some stuff that is different. Your game may or may not “just work” ™
We have learned a lot over the last couple of years. If I had a time machine, I would have many suggestions to send back to when we were originally designing this stuff! But from where we are now, it’s a tough choice. Do we leave things as they are, knowing our design has flaws, or do we fix problems and take the cost of a breaking change?
I don't think the language/framework should suffer as a result of not wanting to break code, especially where it is possible to have multiple framework versions present.
Taking the middle road, or putting off breaking changes can be dangerous too, as it can make more important changes further on down the line much more difficult. I would have to venture a guess that from one version to another (minor versions likely, eg 3.0 => 3.1) there are often not many proposed breaking changes, so it would be better to make them as they arise. Making one breaking change each minor version would be favorable to making 5 or 6 on a major version as it allows users to adapt at a more incremental rate.
As for the community, authors are always looking for a reason to pump out a new edition, so they shouldn't complain ;)
Tutorials and samples are often updated by the authors, but if not, it leaves an important exercise to the user to bring the code they created using the sample/tutorial up to the new version. There is often a level of understanding missing when someone creates something according to a tutorial or sample, as much of it can be blindly following the steps, or even using direct copy&paste, and being forced to go in and fix bugs directly when a breaking change is made will increase the users understanding of the code and language.
Ruby has made it apparent they value overall language quality over any drawbacks of breaking changes (eg string handling from 1.8 => 1.9) and I feel the language itself benefits greatly from it.
Even if it results in a million people asking the same question ("Why don't renderstates work anymore?"), that is still a million people flocking to community sites for information, strengthening the community itself.
Don't fall into the ways of C++/Java, always put quality/correctness as the top priority, and let the users adapt. (You are doing more than enough by supporting multiple framework versions.)
I agree with enntwo. Better to improve the framework even when it breaks existing apps. It would be nice, however, if the changes were clearly identified to minimize the pain for developers.
I also agree with enntwo. Things move so quick these days it's hardly worth looking backward.
We'll all keep up!
Agree with enntwo too. Real time graphics is always been a rapid revolution field. Compare to the benefit of new feature, I don't mind breaking change as long as the main application model remains still.
I think one of the major problems with breaking changes is when they are discussed after the event. Ideally, the process should be:
1) Community reaction. I.e. "Hey guys, we're thinking about changing X to Y, so that we can do Z. What does everyone think about this?"
2) After a final decision has been made on what is going to change, a post should be made to the user base letting them know what is going to change, why, and what they should expect to have to do to update any existing code.
3) This information should be included in the official documentation for the existing framework, so that someone reading it will be able to plan for the changes.
4) When the new framework is released, the community will be ready and should be able to upgrade easily enough, only making minor modifications to deal with last minute changes.
What normally seems to happen is that only when a new framework is released do people find out about the changes. I'd much rather have a situation where I could plan ahead when coding because I knew what fundamental changes were being implemented in the framework, then possibly only have to make minor tweaks to cope with last minute changes, than have to deal with a sudden surge of major changes when the new framework is released. I understand that companies need to keep certain things under wraps until "the big announcement", but a steady stream of information to the user base is far better than getting it all at once and having to deal with it.
(When I say framework here, I don't mean the XNA framework, just frameworks / SDKs in general.)
tl;dr: Let the community know what to expect in plenty of time, and they won't mind so much :)
Yeah, I'd have to agree too. I'd rather see XNA 4.0 "live up" to everything it can be even if it means old stuff won't build anymore. As long as the changes are well described somewhere, along with forums to discuss the changes, I think it's worth it.
I think it is important to achieve a middle ground in this respect. Putting off changes too long can lead to inconsistant, "ugly" and obsolete/stagnent APIs(a couple of APIs spring to mind here).
However too many small changes which break things can be problematic too. Smaller non breaking improvements are often a good thing however IMHO, they tend to improve the API by filling in short comings.
I think such things however depend a lot on the time frame of releases (probably the main issue with the PhysX API, a very long time between major releases, which is presumably when breaking changes will be able to happen).
The support in the platform for multiple versions(eg this seems problematic for a plain C API like OpenGL).
The rate of change of the technology which the API is trying to expose. For example this is probably the major reason D3D has been successful, it has changed along with(or before) hardware/software graphics technology changes.
Do we leave things as they are, knowing our design has flaws, or do we fix problems and take the cost of a breaking change?
When an API is getting to a point that you cannot significatively improve it unless you change its design/implementation, be the bold and the brave and go for the breaking change!!!
"ways we could improve the XNA Framework API"
I have worked with many game frameworks since the middle of the 90s, what's missing from almost all of them is simple but correct collision detection. This is one of the hardest parts of making a game with lots of math required, yet every game needs it. A few more small but simple to use collision detection routines in the framework would be a great help.
Absolutely not a physics engine like farseer or box2D, that is much to complicated and don't work well for arcade style games, but something that helps you write pong (circle-rect) or space invaders (tunneling bullets). Cirle - rect collision, something for tunneling problems and a built in pixel perfect PointInTexture2D would be a great help for everyone starting out I think.
Break break break! I don't care if I have to change my existing code; I'll be happy to update it to the latest and greatest. I don't want to work with an outdated and convoluted API any more than you want to.
I share the same frustrations with the company I work for now. The framework I built for our application is now being used by many and now it's holding back innovation. I'm making it legacy code and moving on to a new framework.
I vote for bigger breaking changes. If your app is broke with the next big version, you can always stay with the one that works.
Breaking changes hurt for a moment, backwards compatibility is hell forever.
Sure, breaking changes shouldn't be undertaken lightly, and there are things you can do to mitigate the pain, but they're absolutely the right thing to do from the standpoint of the long-term health of the platform.
Let me try to put it into as few words as possible:
"If you break it, break it good."
(maybe some more words as an explanation, just to be very clear: Do whatever it takes to not break code. Once the time comes that this is infeasible, make a big nice breaking change and correct _all_ the evil in the API)
Jamie's guideline to breaking changes.
If it is a major release (2.0) then we do the best job redesigning the API to include the changes/fixes we want.
If it is a minor release (2.1) then we should not introduce breaking changes.