Explanation
First let me briefly define how I’m using terms (I might be bending their typical use a little):
When I talk about semantic breaking changes, I’m referring to a change in the meaning/behavior of a public API. It may require changes to consumer code, but it isn’t immediately obvious, since nothing ‘broke’. Changes are only required because the assumptions made about preconditions/behavior are no longer valid.
This is in contrast to syntactic breaking changes by which I’m referring to changes in the public API that require the calling code to change the way it calls the API. This kind of breaking change is obvious since it usually results in compile errors which have to be fixed.
Example
Let’s say I’m writing a micro-ORM that given a POCO object, a database connection, and a query will map the results into the object for you. The public API, is fairly simple:
Query<T>(connection As IDbConnection, sql As String)
I’m very unlikely to have a syntactic change in how you interact this tool. However, since there is a lot of behavior bound up in that simple statement, I’m very likely to have semantic changes.
Question
Since there’s no syntax change, how do I effectively communicate to consumers of my API that there has been a semantic change? Should I always bundle up semantic changes in syntatic changes so there’s no question that something has changed?
If semantic versioning encompasses semantic as well as syntactic changes, then that goes a long way to solving the issue. But it would still be possible for developers to build against a newer version, not notice any compile errors, assume things will be fine (since they haven’t read the docs), and introduce bugs due to semantic changes in my API.
It depends on whether the semantic change would break any existing code.
If everything that worked before will still work in the same way (as determined by a robust set of regression tests), then there’s no need to change anything. If you provide more functionality, which the user can choose to take advantage of, then there’s no need to change anything. However, if the behavior change would break existing logic, then you shouldn’t make that change. Instead, create a new variant of the function which differs syntactically (parameters, return values, etc), and deprecate the old one.
2