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

Typescript should follow semantic versioning #14116

Closed
jbgraug opened this Issue Feb 16, 2017 · 14 comments

Comments

Projects
None yet
@jbgraug

jbgraug commented Feb 16, 2017

I guess this can be considered as a bug as it is breaking my apps.
If version 2.1.0 has breaking changes, why don't set it to 3.0.0? Semantic versioning, right?
Typescript lives and has important role in the nodejs ecosystem, therefore should follow some basic rules.
We can not use the npm's semver features to block only major changes.

TypeScript Version: 2.1.1 / nightly (2.2.0-dev.201xxxxx)

2.1.0
Code

// A *self-contained* demonstration of the problem follows...
No need

Expected behavior:
Follow semantic versioning rules
Actual behavior:
Just follows marketing versioning rules
can be related to #6520 created a year ago and closed without being solved

@mhegazy

This comment has been minimized.

Show comment
Hide comment
@mhegazy

mhegazy Feb 16, 2017

Contributor

TypeScript never claimed to follow semantic versioning, in the sense that breaking changes imply major versions.

TypeScript, however, promises no breaking changes after a stable release. so no breaking changes between 2.1.5 and 2.1.6, 2.1.*.

My recommendation is fix your version of typescript to major.minor instead of just major. e.g. ^2.1 and not ^2

Contributor

mhegazy commented Feb 16, 2017

TypeScript never claimed to follow semantic versioning, in the sense that breaking changes imply major versions.

TypeScript, however, promises no breaking changes after a stable release. so no breaking changes between 2.1.5 and 2.1.6, 2.1.*.

My recommendation is fix your version of typescript to major.minor instead of just major. e.g. ^2.1 and not ^2

@jbgraug

This comment has been minimized.

Show comment
Hide comment
@jbgraug

jbgraug Feb 17, 2017

Thanks for the early response @mhegazy .
As you mentioned, blocking the versions is easy to fix in my projects (I was being a bit ironic.).
However, as a developer i don't feel comfortable having to memorise a different set of rules for each package as the number of packages used grows very quickly.
I guess having typescript follow semantic versioning would be a nice to have feature.

jbgraug commented Feb 17, 2017

Thanks for the early response @mhegazy .
As you mentioned, blocking the versions is easy to fix in my projects (I was being a bit ironic.).
However, as a developer i don't feel comfortable having to memorise a different set of rules for each package as the number of packages used grows very quickly.
I guess having typescript follow semantic versioning would be a nice to have feature.

@RyanCavanaugh

This comment has been minimized.

Show comment
Hide comment
@RyanCavanaugh

RyanCavanaugh Feb 17, 2017

Member

The trade-off for getting millions of dollars of engineering investment in the TypeScript project is that marketing gets to control version numbers to a certain extent.

It's not really an unalloyed good anyway. If we followed semver rules exactly, literally every single release would be a major version bump. Any time we produced the wrong type or emitted the wrong code or failed to issue a correct error, that's a breaking change, and we fix dozens of bugs like that in every release. The middle digit just isn't useful for TypeScript in a strict semver interpretation.

Member

RyanCavanaugh commented Feb 17, 2017

The trade-off for getting millions of dollars of engineering investment in the TypeScript project is that marketing gets to control version numbers to a certain extent.

It's not really an unalloyed good anyway. If we followed semver rules exactly, literally every single release would be a major version bump. Any time we produced the wrong type or emitted the wrong code or failed to issue a correct error, that's a breaking change, and we fix dozens of bugs like that in every release. The middle digit just isn't useful for TypeScript in a strict semver interpretation.

@niieani

This comment has been minimized.

Show comment
Hide comment
@niieani

niieani Feb 19, 2017

NPM should simply allow for descriptive marketing versions as a forth group. Then we'd have the best of both worlds, i.e.

       marketing
           ∨
TypeScript 2.34.2.1 
             ∧∧ ∧ ∧
          major ∧ patch
                ∧
              minor

You would simply skip the marketing version while installing, i.e. npm install typescript@^34 since it would hold no semantic meaning, i.e. bumping marketing wouldn't reset the major counter.

niieani commented Feb 19, 2017

NPM should simply allow for descriptive marketing versions as a forth group. Then we'd have the best of both worlds, i.e.

       marketing
           ∨
TypeScript 2.34.2.1 
             ∧∧ ∧ ∧
          major ∧ patch
                ∧
              minor

You would simply skip the marketing version while installing, i.e. npm install typescript@^34 since it would hold no semantic meaning, i.e. bumping marketing wouldn't reset the major counter.

@BenSayers

This comment has been minimized.

Show comment
Hide comment
@BenSayers

BenSayers Apr 7, 2017

I'm concerned that not following semver is creating unnecessary friction for TypeScript consumers who are opted in to having their builds broken whenever TypeScript releases a minor version as npm locks down to only major versions by default. The Microsoft Edge team has figured out how to do their marketing despite bumping the major version a few times a month (currently up to v38), I think TypeScript should give serious consideration to doing the same for the good of its consumers.

BenSayers commented Apr 7, 2017

I'm concerned that not following semver is creating unnecessary friction for TypeScript consumers who are opted in to having their builds broken whenever TypeScript releases a minor version as npm locks down to only major versions by default. The Microsoft Edge team has figured out how to do their marketing despite bumping the major version a few times a month (currently up to v38), I think TypeScript should give serious consideration to doing the same for the good of its consumers.

@aluanhaddad

This comment has been minimized.

Show comment
Hide comment
@aluanhaddad

aluanhaddad Apr 7, 2017

Contributor

Personally, I think it is a good idea to always specify exact versions of critical stack components such as compilers, loaders, bundlers, and of course frameworks.

There are not that many of these tools in a single project and they do not tend to release more than once a week or so. This makes explicitly upgrading a relatively straightforward process.

Also, reading the changelogs for updates to such key dependencies is almost certainly something that one should be doing.

That said, I think it's fine to version more liberally. Each project is different in this regard.

The trade-off for getting millions of dollars of engineering investment in the TypeScript project is that marketing gets to control version numbers to a certain extent.

That is a trade well worth making.

Furthermore, TypeScript is by no means the only project that does this.

I think any project that is high profile enough is likely subject to this, at least to some extent.

Even if it is not the marketing department, it may be the maintainers' own self-consciousness that leads to such versioning.

Any time we produced the wrong type or emitted the wrong code or failed to issue a correct error, that's a breaking change, and we fix dozens of bugs like that in every release.

TypeScript really releases at a blisteringly unprecedented pace for a programming language so I think this is somewhat inevitable. I also think it's common across almost all software. Minor versions of most software contain breaking changes, but they often go unnoticed. The more high-profile the project, the more users that has, the more likely it is that this will be noticed.

The TypeScript team do an incredible job and they ship a wonderfully high quality product.

Contributor

aluanhaddad commented Apr 7, 2017

Personally, I think it is a good idea to always specify exact versions of critical stack components such as compilers, loaders, bundlers, and of course frameworks.

There are not that many of these tools in a single project and they do not tend to release more than once a week or so. This makes explicitly upgrading a relatively straightforward process.

Also, reading the changelogs for updates to such key dependencies is almost certainly something that one should be doing.

That said, I think it's fine to version more liberally. Each project is different in this regard.

The trade-off for getting millions of dollars of engineering investment in the TypeScript project is that marketing gets to control version numbers to a certain extent.

That is a trade well worth making.

Furthermore, TypeScript is by no means the only project that does this.

I think any project that is high profile enough is likely subject to this, at least to some extent.

Even if it is not the marketing department, it may be the maintainers' own self-consciousness that leads to such versioning.

Any time we produced the wrong type or emitted the wrong code or failed to issue a correct error, that's a breaking change, and we fix dozens of bugs like that in every release.

TypeScript really releases at a blisteringly unprecedented pace for a programming language so I think this is somewhat inevitable. I also think it's common across almost all software. Minor versions of most software contain breaking changes, but they often go unnoticed. The more high-profile the project, the more users that has, the more likely it is that this will be noticed.

The TypeScript team do an incredible job and they ship a wonderfully high quality product.

@gcnew

This comment has been minimized.

Show comment
Hide comment
@gcnew

gcnew Apr 7, 2017

Contributor

I can't agree with @aluanhaddad more. Personally, I think using language version 81 and browser version 127 is terrible. It looks ugly and these high numbers quickly become meaningless. In the browser case that's the intention - forcing consumers to update to the latest version. However, for a language it's out of place and makes following new features and important changes extremely hard. Every version, no matter how big or small, looks the same way as every other. Flow has fallen in that trap and it doesn't seem to reap many benefits out of it.

For TypeScript, if you still want automatic updates without worrying too much, just lock the minor version in and everything will fall into place.

Contributor

gcnew commented Apr 7, 2017

I can't agree with @aluanhaddad more. Personally, I think using language version 81 and browser version 127 is terrible. It looks ugly and these high numbers quickly become meaningless. In the browser case that's the intention - forcing consumers to update to the latest version. However, for a language it's out of place and makes following new features and important changes extremely hard. Every version, no matter how big or small, looks the same way as every other. Flow has fallen in that trap and it doesn't seem to reap many benefits out of it.

For TypeScript, if you still want automatic updates without worrying too much, just lock the minor version in and everything will fall into place.

@RyanCavanaugh

This comment has been minimized.

Show comment
Hide comment
@RyanCavanaugh

RyanCavanaugh Apr 7, 2017

Member

I mean, even semver's own definition of "breaking change" is arguably wrong. Minor updates can add new functionality under new properties, and new properties can break existing codepaths because JS is full of do-x-if-y-property-is-present patterns. Fixing a performance bug, which would in theory be a bugfix version update, could cause two async operations which previously always resolved in one order to instead always resolve in another order.

It is simply not the case that you can safely upgrade code, with semver as used today by normal package maintainers, from 34.1 of some library to 34.9 and be guaranteed that your program will still behave the same way.

What semver means in practice is that the major version bump is "You will probably need to update your code in a lot of places", and the minor version bump is "You should always be OK for the most part". TypeScript never makes updates of the first kind. We only make compat-breaking changes where we believe you should always be OK modulo a small number of fixes we think you'll be happy making (because we found new bugs).

We're not going to take a major version bump because there was a bug in the compiler where it failed to identify early errors, even though that's technically a breaking change - we think you should be "along for the ride" on that one if you didn't shrinkwrap. That's how semver is used in practice by everyone anyway.

Member

RyanCavanaugh commented Apr 7, 2017

I mean, even semver's own definition of "breaking change" is arguably wrong. Minor updates can add new functionality under new properties, and new properties can break existing codepaths because JS is full of do-x-if-y-property-is-present patterns. Fixing a performance bug, which would in theory be a bugfix version update, could cause two async operations which previously always resolved in one order to instead always resolve in another order.

It is simply not the case that you can safely upgrade code, with semver as used today by normal package maintainers, from 34.1 of some library to 34.9 and be guaranteed that your program will still behave the same way.

What semver means in practice is that the major version bump is "You will probably need to update your code in a lot of places", and the minor version bump is "You should always be OK for the most part". TypeScript never makes updates of the first kind. We only make compat-breaking changes where we believe you should always be OK modulo a small number of fixes we think you'll be happy making (because we found new bugs).

We're not going to take a major version bump because there was a bug in the compiler where it failed to identify early errors, even though that's technically a breaking change - we think you should be "along for the ride" on that one if you didn't shrinkwrap. That's how semver is used in practice by everyone anyway.

@mhegazy mhegazy closed this Apr 19, 2017

@eddieajau

This comment has been minimized.

Show comment
Hide comment
@eddieajau

eddieajau Jun 15, 2017

Can we at least get a section on "TypeScript and Semver" added to the docs? The fundamental problem is that npm install --save typescript will add `"typescript":"^2.4.0" by default. Consumers need to be aware that this is dangerous and you need to change it to "~2.4.0" (tilde, not carat). I'm happy to do the PR if you can advise on where you want such information.

But for what it's worth:

"You will probably need to update your code in a lot of places" ... TypeScript never makes updates of the first kind.

The Promise changes in 2.4 is resulting in lots of little changes all over the place. I'm not saying I don't agree with the changes, but they are there nonetheless. I've been caught only because I've [wrongly] assumed that TypeScript was following Semver.

eddieajau commented Jun 15, 2017

Can we at least get a section on "TypeScript and Semver" added to the docs? The fundamental problem is that npm install --save typescript will add `"typescript":"^2.4.0" by default. Consumers need to be aware that this is dangerous and you need to change it to "~2.4.0" (tilde, not carat). I'm happy to do the PR if you can advise on where you want such information.

But for what it's worth:

"You will probably need to update your code in a lot of places" ... TypeScript never makes updates of the first kind.

The Promise changes in 2.4 is resulting in lots of little changes all over the place. I'm not saying I don't agree with the changes, but they are there nonetheless. I've been caught only because I've [wrongly] assumed that TypeScript was following Semver.

@daprahamian

This comment has been minimized.

Show comment
Hide comment
@daprahamian

daprahamian Jun 21, 2017

I'd like to add a related question: should type definition files be compatible across all of 2.x.x? Can someone compile their library in 2.2, and have it work when someone pulls it in and compiles with 2.1?

daprahamian commented Jun 21, 2017

I'd like to add a related question: should type definition files be compatible across all of 2.x.x? Can someone compile their library in 2.2, and have it work when someone pulls it in and compiles with 2.1?

@DynConcepts

This comment has been minimized.

Show comment
Hide comment
@DynConcepts

DynConcepts Jul 10, 2017

I too would prefer SemVer, and yes I know the majority of publishers are not "doing it right"...But lets look at an earlier comment:

"If we followed semver rules exactly, literally every single release would be a major version bump. Any time we produced the wrong type or emitted the wrong code or failed to issue a correct error, that's a breaking change, and we fix dozens of bugs like that in every release. "

Writing quality software is hard and requires investment. The above can be mitigated with improved testing and documentation.

Remember the Agile principle: "the art of maximizing the amount of work not done--is essential. ". This should not mean the amount of work done by a team to get something out the door. Rather it should be a global optimization to minimize the work required by all stakeholders globally and across type - the TOTAL work.

DynConcepts commented Jul 10, 2017

I too would prefer SemVer, and yes I know the majority of publishers are not "doing it right"...But lets look at an earlier comment:

"If we followed semver rules exactly, literally every single release would be a major version bump. Any time we produced the wrong type or emitted the wrong code or failed to issue a correct error, that's a breaking change, and we fix dozens of bugs like that in every release. "

Writing quality software is hard and requires investment. The above can be mitigated with improved testing and documentation.

Remember the Agile principle: "the art of maximizing the amount of work not done--is essential. ". This should not mean the amount of work done by a team to get something out the door. Rather it should be a global optimization to minimize the work required by all stakeholders globally and across type - the TOTAL work.

@daprahamian

This comment has been minimized.

Show comment
Hide comment
@daprahamian

daprahamian Jul 11, 2017

If it is not possible to follow semver, could we come up with some sort of way to enable backwards compatibility / legacy behavior in our code? I am currently dealing with two major issues that I view as breaking changes:

Consuming a library where the .d.ts files are compiled in TS version higher than my project.

Example: Using TS 2.0, consuming a project that exports an interface with a member that is of type object.

A few ideas on how to fix this:

  • Allow TypeScript to compile to a previous version's output.
  • Allow TypeScript to compile to multiple versions of types output, and then include a root file that routes the consuming compiler to the right definition using some sort of markup (comments, conditional compilation, etc.)

Consuming a library where the .d.ts files are compiled in a TS version lower than my project.

Some examples include:

All of these issues stem from stricter type checking introduced in a later version of TS. This could probably be mitigated by doing the following:

  1. Having .d.ts files contian the compiler version via comments
  2. Having the compiler check the imported code's compiler version, and only apply new/stricter rules if they existed in the compiled version.

daprahamian commented Jul 11, 2017

If it is not possible to follow semver, could we come up with some sort of way to enable backwards compatibility / legacy behavior in our code? I am currently dealing with two major issues that I view as breaking changes:

Consuming a library where the .d.ts files are compiled in TS version higher than my project.

Example: Using TS 2.0, consuming a project that exports an interface with a member that is of type object.

A few ideas on how to fix this:

  • Allow TypeScript to compile to a previous version's output.
  • Allow TypeScript to compile to multiple versions of types output, and then include a root file that routes the consuming compiler to the right definition using some sort of markup (comments, conditional compilation, etc.)

Consuming a library where the .d.ts files are compiled in a TS version lower than my project.

Some examples include:

All of these issues stem from stricter type checking introduced in a later version of TS. This could probably be mitigated by doing the following:

  1. Having .d.ts files contian the compiler version via comments
  2. Having the compiler check the imported code's compiler version, and only apply new/stricter rules if they existed in the compiled version.
@jsamr

This comment has been minimized.

Show comment
Hide comment
@jsamr

jsamr Nov 23, 2017

And what about a new package.json property : "breaking" which values can be "MAJOR" (default), "MINOR" ?
See npm/npm#19231

jsamr commented Nov 23, 2017

And what about a new package.json property : "breaking" which values can be "MAJOR" (default), "MINOR" ?
See npm/npm#19231

@franklinyu

This comment has been minimized.

Show comment
Hide comment
@franklinyu

franklinyu Jun 8, 2018

Fixing a performance bug, which would in theory be a bugfix version update, could cause two async operations which previously always resolved in one order to instead always resolve in another order.

If the resolution order is documented, then indeed it is breaking change and should bump major version; otherwise it is implementation details and user is responsible for depending on this.

franklinyu commented Jun 8, 2018

Fixing a performance bug, which would in theory be a bugfix version update, could cause two async operations which previously always resolved in one order to instead always resolve in another order.

If the resolution order is documented, then indeed it is breaking change and should bump major version; otherwise it is implementation details and user is responsible for depending on this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment