-
-
Notifications
You must be signed in to change notification settings - Fork 5.2k
[Review] Added detailed Backwards Compatibility Promise text #3439
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
840073c
7320ed0
dacd7ce
79ca9f7
0e925cb
44ecf16
afadaab
345410c
a3ad08c
31ab2db
502ed95
4c5a55d
c6e850d
db76288
54fd836
00c6ebe
efd3911
dcbe79a
af3a645
be76644
dfb3e8b
6501a35
0c6420f
69768dd
5a160c5
ef1f021
6d9edf1
8c6c7bf
4868452
e11335f
25443c0
11bb879
fd1d912
bdd3c03
2320906
90c4de6
be2251c
ce58ee9
0717192
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,33 +1,42 @@ | ||
Our Backwards Compatibility Promise | ||
=================================== | ||
|
||
As of Symfony 2.3, we promise you backwards compatibility (BC) for all further | ||
2.x releases. Ensuring smooth upgrades of your projects is our first priority. | ||
However, backwards compatibility comes in many different flavors. | ||
If you are using Symfony, we promise you backwards compatibility (BC) for all | ||
major releases (2.x, 3.x, ...). Ensuring smooth upgrades of your projects is our | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we decided to use "etc" instead of "..." in the text |
||
first priority. However, backwards compatibility comes in many different flavors. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The docs are all written in the third person perspective (except from the Quick Tour). This document should follow that as well There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this paragraph should be extended a little bit to mention major versions (3.x, 4.x) and the fact that these versions might break BC (mentioning semantic versioning and linking to the spec might help as well). We should probably also make it clear that breaking BC is not the end of the world. A lot of developers associate breaking BC as what happened in the past with Symfony 1/2 or ZF 1/2 BC breaks. We should say that breaking BC can lead to simple and fast upgrade as well. I suppose that we need to define a BC break here. |
||
|
||
.. note:: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think a |
||
|
||
This promise was introduced with Symfony 2.3 and does not apply to previous | ||
versions of Symfony. | ||
|
||
This page has two different target audiences: If you are using Symfony, it will | ||
tell you how to make sure that you will be able to upgrade smoothly to all | ||
future 2.x versions. If you are contributing to Symfony, this page will tell you | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. again, mentioning a specific |
||
the rules that you need to follow to ensure smooth upgrades for our users. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what about adding links to the sections (using the section headline as the reference name)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not too fond of that. Links here suggest that there is external, further information about that, when in reality the user is linked to the same page. |
||
|
||
.. note:: | ||
|
||
This promise is in trial until April 15th, 2014. Until then, we may change | ||
parts of it if we discover problems or limitations. | ||
|
||
|
||
Using Symfony Code | ||
------------------ | ||
|
||
You are using Symfony in your projects? Then stick to the guidelines in this | ||
section in order to guarantee smooth upgrades to all future 2.x versions. | ||
|
||
If you are using Symfony in your projects, the following guidelines will help | ||
you to ensure smooth upgrades to all future minor releases of Symfony (such as | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. both sounds fine, I think I even prefer the current version There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah i think so too, just put it up to clarify and because google results double by the thousands when removing the to |
||
2.5, 2.6 and so on). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As you will add definitions of what is minor version and what is a major one in the introduction paragraph (see my comments above), what about removing the 2.x lists everywhere to avoid tying this document to Symfony 2.x only. |
||
|
||
Using Our Interfaces | ||
~~~~~~~~~~~~~~~~~~~~ | ||
|
||
In Symfony, we distinguish between regular and API interfaces. API interfaces | ||
are marked with an ``@api`` tag in their source code. For example:: | ||
All interfaces shipped with Symfony can be used in type hints. You can also call | ||
any of the methods that they declare. We guarantee that we won't break code that | ||
sticks to these rules. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not really related to this document, but that makes me even more reluctant to add interfaces for everything (see my comments on your recent PR on validators). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why? I don't really understand. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think he is saying that is a trade off, not all interfaces have the same importance or relevance. The outer or higher level of an interface should be regarded as strongly tied to the promise, whereas the inner or lesser level interfaces should allow some flexibility on the promise. |
||
|
||
.. note:: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. again, I think |
||
|
||
The exception to this rule are interfaces tagged with ``@internal``. Such | ||
interfaces should never be used or implemented. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe emphasis ( |
||
|
||
If you want to implement an interface, you should first make sure that the | ||
interface is an API interface. You can recognize API interfaces by the ``@api`` | ||
tag in their source code:: | ||
|
||
/** | ||
* HttpKernelInterface handles a Request to convert it to a Response. | ||
|
@@ -39,40 +48,51 @@ are marked with an ``@api`` tag in their source code. For example:: | |
interface HttpKernelInterface | ||
{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this will break the highlighting, please do something like: interface HttpKernelInterface
{
// ...
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||
|
||
In simple words, the difference between regular and API interfaces is that you | ||
can implement API interfaces yourself and we will guarantee full backwards | ||
compatibility. The same is not true for regular interfaces: We may, for example, | ||
add an optional parameter or a new method to them and thus break your own | ||
implementation. | ||
|
||
In detail, we guarantee full backwards compatibility for the following use | ||
cases: | ||
|
||
============================================== ============== ============== | ||
Use Case Regular API | ||
============================================== ============== ============== | ||
Type hint against Yes Yes | ||
Call method Yes Yes | ||
**In Implementing Classes** | ||
Implement method No [1]_ Yes | ||
Add custom method No [1]_ Yes | ||
Add custom method parameter No [1]_ Yes | ||
Add parameter default value Yes Yes | ||
============================================== ============== ============== | ||
If you implement an API interface, we promise that we won't ever break your | ||
code. Regular interfaces, by contrast, should never be implemented, because if | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
we add a new method to the interface or add a new optional parameter to the | ||
signature of a method, we would generate a fatal error in your application. | ||
|
||
The following table explains in detail which use cases are covered by our | ||
backwards compatibility promise: | ||
|
||
+-----------------------------------------------+---------------+---------------+ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe put this table in a (sub) section "Summarize"? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think having a section ("Using or interface") with text and then just one subsection makes sense. |
||
| Use Case | Regular | API | | ||
+-----------------------------------------------+---------------+---------------+ | ||
| If you... | Then we guarantee BC... | | ||
+===============================================+===============+===============+ | ||
| Type hint against the interface | Yes | Yes | | ||
+-----------------------------------------------+---------------+---------------+ | ||
| Call a method | Yes | Yes | | ||
+-----------------------------------------------+---------------+---------------+ | ||
| **If you implement the interface and...** | **Then we guarantee BC...** | | ||
+-----------------------------------------------+---------------+---------------+ | ||
| Implement a method | No [1]_ | Yes | | ||
+-----------------------------------------------+---------------+---------------+ | ||
| Add a parameter to an implemented method | No [1]_ | Yes | | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't get this one. Let's say you have this interface: interface Foo
{
function bar($a);
} Here is my implementation: class ConcreteFoo implements Foo
{
function bar($a, $b = 'a')
{
// here, I've added $b
}
} Adding There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, that's what I mean. I will change "parameter" to "argument" everywhere. This is very useful if you want to create a class that satisfies an interface, but wants to expose additional configuration options in that method. For a simple example: interface ValueBagInterface
{
public function get($name);
}
class MyValueBag implements ValueBagInterface
{
public function get($key, $default = null)
{
// ...
}
} My class can be used anywhere where that interface is expected, but when using the class directly in my code, I can take advantage of the additional argument. |
||
+-----------------------------------------------+---------------+---------------+ | ||
| Add a default value to a parameter | Yes | Yes | | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not really a promise that we do, but rather a promise from PHP, no? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. I think it should be included for sake of clarity though. |
||
+-----------------------------------------------+---------------+---------------+ | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The meaning of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For instance, what does There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This whole section is targeted at Symfony users (not developers; the dev section is below). So "Add custom method" means that a user should not add methods that do not exist in our interface (i.e. we cannot guarantee BC). Example: /** regular interface, not API */
interface SymfonyInterface
{
public function foo();
}
class MyImplementation implements SymfonyInterface
{
// BC guaranteed for this method
public function foo() { ... }
// BC not guaranteed for this method (if we add bar() to SomeInterface, but with a different signature)
public function bar() { ... }
} |
||
.. note:: | ||
|
||
If you need to do any of the things marked with "No" above, feel free to | ||
ask us whether the ``@api`` tag can be added on the respective Symfony code. | ||
For that, simply open a `new ticket on GitHub`_. | ||
|
||
Interfaces or interface methods tagged with ``@internal`` should never be | ||
implemented or used. | ||
|
||
If you think that one of our regular interfaces should have an ``@api`` tag, | ||
put your request into a `new ticket on GitHub`_. We will then evaluate | ||
whether we can add the tag or not. | ||
|
||
There was a pr A377 oblem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Small formatting nitpick: you have a bunch of double-newlines which should be converted to single-newlines. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I left them in before headings. I'll remove them however. |
||
Using Our Classes | ||
~~~~~~~~~~~~~~~~~ | ||
|
||
All classes provided by Symfony may be instantiated and accessed through their | ||
public methods and properties. | ||
|
||
.. note:: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same comments to this directive as the previous one (both caution and emphasis) |
||
|
||
Classes, properties and methods that bear the tag ``@internal`` as well as | ||
the classes located in the various ``*\\Tests\\`` namespaces are an | ||
exception to this rule. They are meant for internal use only and should not | ||
be accessed by your own code. | ||
|
||
Just like with interfaces, we also distinguish between regular and API classes. | ||
Like API interfaces, API classes are marked with an ``@api`` tag:: | ||
|
||
|
@@ -87,57 +107,69 @@ Like API interfaces, API classes are marked with an ``@api`` tag:: | |
{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here (missing closing curly bracket) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||
|
||
The difference between regular and API classes is that we guarantee full | ||
backwards compatibility if you extend an API class and override its methods, | ||
but not for regular classes. In regular classes, we may for example add an | ||
optional parameter to a method and thus break your overridden method. | ||
|
||
Again, here are the details about which use cases we guarantee full backwards | ||
compatibility for: | ||
|
||
============================================== ============== ============== | ||
Use Case Regular API | ||
============================================== ============== ============== | ||
Type hint against Yes Yes | ||
Create instance Yes Yes | ||
Extend Yes Yes | ||
Access public property Yes Yes | ||
Call public method Yes Yes | ||
**In Extending Classes** | ||
Access protected property No [1]_ Yes | ||
Call protected method No [1]_ Yes | ||
Override public property Yes Yes | ||
Override protected property No [1]_ Yes | ||
Override public method No [1]_ Yes | ||
Override protected method No [1]_ Yes | ||
Add custom property No No | ||
Add custom method No No | ||
Add custom method parameter No [1]_ Yes | ||
Add parameter default value Yes Yes | ||
============================================== ============== ============== | ||
|
||
.. note:: | ||
|
||
If you need to do any of the things marked with "No" above, feel free to | ||
ask us whether the ``@api`` tag can be added on the respective Symfony code. | ||
For that, simply open a `new ticket on GitHub`_. | ||
backwards compatibility if you extend an API class and override its methods. We | ||
can't give the same promise for regular classes, because there we may, for | ||
example, add an optional parameter to a method. Consequently, the signature of | ||
your overridden method won't match anymore and generate a fatal error. | ||
|
||
In some cases, only specific properties and methods are tagged with the ``@api`` | ||
tag, even though their class is not. In these cases, we guarantee full backwards | ||
compatibility for the tagged properties and methods (as indicated in the column | ||
"API" above), but not for the rest of the class. | ||
"API" below), but not for the rest of the class. | ||
|
||
To be on the safe side, check the following table to know which use cases are | ||
covered by our backwards compatibility promise: | ||
|
||
+-----------------------------------------------+---------------+---------------+ | ||
| Use Case | Regular | API | | ||
+-----------------------------------------------+---------------+---------------+ | ||
| If you... | Then we guarantee BC... | | ||
+===============================================+===============+===============+ | ||
| Type hint against the class | Yes | Yes | | ||
+-----------------------------------------------+---------------+---------------+ | ||
| Create a new instance | Yes | Yes | | ||
+-----------------------------------------------+---------------+---------------+ | ||
| Extend the class | Yes | Yes | | ||
+-----------------------------------------------+---------------+---------------+ | ||
| Access a public property | Yes | Yes | | ||
+-----------------------------------------------+---------------+---------------+ | ||
| Call a public method | Yes | Yes | | ||
+-----------------------------------------------+---------------+---------------+ | ||
| **If you extend the class and...** | **Then we guarantee BC...** | | ||
+-----------------------------------------------+---------------+---------------+ | ||
| Access a protected property | No [1]_ | Yes | | ||
+-----------------------------------------------+---------------+---------------+ | ||
| Call a protected methods | No [1]_ | Yes | | ||
+-----------------------------------------------+---------------+---------------+ | ||
| Override a public property | Yes | Yes | | ||
+-----------------------------------------------+---------------+---------------+ | ||
| Override a protected property | No [1]_ | Yes | | ||
+-----------------------------------------------+---------------+---------------+ | ||
| Override a public method | No [1]_ | Yes | | ||
+-----------------------------------------------+---------------+---------------+ | ||
| Override a protected method | No [1]_ | Yes | | ||
+-----------------------------------------------+---------------+---------------+ | ||
| Add a new property | No | No | | ||
+-----------------------------------------------+---------------+---------------+ | ||
| Add a new method | No | No | | ||
+-----------------------------------------------+---------------+---------------+ | ||
| Add a parameter to an overridden method | No [1]_ | Yes | | ||
+-----------------------------------------------+---------------+---------------+ | ||
| Add a default value to a parameter | Yes | Yes | | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. f I understand things correctly, this should be |
||
+-----------------------------------------------+---------------+---------------+ | ||
|
||
Classes, properties and methods tagged with ``@internal`` should never be | ||
created, extended or called directly. The same applies to all classes located in | ||
the various ``*\\Tests\\`` namespaces. | ||
.. note:: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is a duplicate note, please create a |
||
|
||
If you think that one of our regular classes should have an ``@api`` tag, | ||
put your request into a `new ticket on GitHub`_. We will then evaluate | ||
whether we can add the tag or not. | ||
|
||
Working on Symfony Code | ||
----------------------- | ||
|
||
Do you want to help us improve Symfony? That's great! However, please stick | ||
to the rules listed below in order to ensure smooth upgrades for our users. | ||
|
||
|
||
Changing Interfaces | ||
~~~~~~~~~~~~~~~~~~~ | ||
|
||
|
@@ -166,7 +198,6 @@ Change parameter type Yes [2]_ [4]_ No | |
Change return type Yes [2]_ [5]_ No | ||
============================================== ============== ============== | ||
|
||
|
||
Changing Classes | ||
~~~~~~~~~~~~~~~~ | ||
|
||
|
@@ -226,7 +257,6 @@ Change parameter type Yes [2]_ [4]_ No | |
Change return type Yes [2]_ [5]_ No | ||
================================================== ============== ============== | ||
|
||
|
||
.. [1] Your code may be broken by changes in the Symfony code. Such changes will | ||
however be documented in the UPGRADE file. | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just use "The Backwards Compatibility Promise", we didn't speak of "our" release cycle neither
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure this feels right here. A promise is something personal, something that has only value if you know who it is from. So I think the first person and use of "our" here makes the document more credible.