You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
To clear any misunderstanding: this proposal aims at not changing the semantics of existing tests.
Proposal Details
#21214 proposed the same but was declined -- mostly because at the time we had no good way of rolling out such a change safely. As this is not the case anymore, and the benefits keep increasing with time (core counts are increasing, codebases and test suites are growing, etc.), I think it would be worth reconsidering it.
The idea would be to make parallel execution the default for modules that specify a go version (not toolchain version) higher than v1.X. Modules with go version below v1.X would still default to serial execution, as today. This would mean that behaviour would not change until a module go version (not toolchain version) is manually updated to v1.X or later.
Individual tests would be given a way to opt-out of parallel execution, similar to how today we allow individual tests to opt-in to parallel execution.
A one-off migration executed automatically when the module is upgraded to a go version after v1.X would ensure that existing semantics (serial execution) are maintained.
I encourage reviewers to focus on the goal when discussing this proposal, i.e. I welcome alternative ways/solutions to achieve the same goal, and will gladly update the proposal if a better solution is suggested. Some discussion is happening in https://gophers.slack.com/archives/C0VP8EF3R/p1747791714490489.
Proposal
Add testing.T.Serial() to allow individual tests to opt out of the new default. Mention that Serial was the default before go v1.X. No-op if the default is serial execution. Panics or fails the test if called after Parallel(). If present, it must be the first statement in the test function.
Update the doc for testing.T.Parallel() to mention that this is the default starting with go v1.X. No-op if the default is parallel execution. Panics or fails the test if called after Serial().
For modules that specify in go.mod a go version equal or greater than v1.X, run the tests in parallel by default; modules that specify an older version would still run tests serially be default.
When a module's go version is updated with go mod tidy (when it triggers the bump) or go mod edit -go=1.Z1 from a v1.Y < v1.X to a version v1.Z >= v1.X, a one-off migration is performed2 on the module, adding Serial() calls to any test that does not have a Parallel() call, and removing Parallel() calls from any test that has it3.
Linters and other tools could start flagging t.Parallel() calls as unneeded for modules with a go.mod go version equal or greater than v1.X.
To guarantee the ordering of serial tests, the compiler should recognize test functions that call t.Serial() and arrange for them to be executed in the order in which they appear in the file. (This is currently the ugliest implementation detail of this proposal; alternatives are most welcome).
FAQ
Will I be forced to rewrite my tests to be runnable in parallel?
No.
A migration tool would automatically turn existing tests like:
thus maintaining the current semantics, even after the migration to the new go version.
For cases in which t.Parallel() is not the first statement with side effects in the function there are two options: either bail out of the migration and warn the user that manual migration is required (but this is pretty disruptive), or drop the t.Parallel() and as a fallback add a t.Serial(), printing a warning about this (as it may slow the test suite).
Users could then, over time, drop the t.Serial from tests that do not really need them.
Worth pointing out that, even without version control, this change is trivially reversible3.
Will this break my tests?
No. (see previous answer for details)
Why should tests be executed in parallel by default?
Because serial tests:
can accidentally hide hidden dependencies between tests
can accidentally hide data races
implicitly endorse the use of global state
are slower to run when part of a large test suite4
Parallel tests being the default nudge the whole ecosystem in a better direction, without forcing anyone to have to write parallel tests.
Isn't this going to be disruptive?
Statistics indicate that the vast majority of modules do not specify the most recent go version (1.24.*), or even just a go module version that was released in the last year.
This would seem to suggest that most existing modules will not be migrated to a go module version where the default is parallel execution. For modules that are migrated, the automated migration would make sure that existing semantics are maintained, therefore not requiring maintainers to spend any extra effort during the migration or later - unless they want to make use of parallel test execution (but this is orthogonal to the change, as it would be the case even if this proposal is not enacted).
I can acknowledge that some users that do not follow go development may be surprised by the new t.Serial() calls added by the migration tool, but I would suggest that a quick online search would almost certainly lead them to an article that explains the rationale.
All the above considered, I would argue that the change may be at most characterized as "surprising", but not really "disruptive".
Footnotes
We could detect also manual edits of go.mod if the module's go version was added to go.sum (or a different "lock" file). In this way the go tool could notice manual modifications of the go version, and either direct the user to run the migration using a dedicated command, or prompt the user that to continue the action a migration is required that will be executed if the user accepts. ↩
Whether the migration is performed silently, or whether the user is prompted to confirm they want to run the migration, is a UX implementation detail. ↩
This migration is trivially reversible even without version control by adding a Parallel() call to any function without Serial() call, and removing all Serial() calls. ↩↩2
This is further made relevant by the increasing core counts of modern infrastructure. Making use of the available cores to reduce test duration is much easier if most/all tests are executed in parallel. While it is true that go test is normally already able to parallelize test execution across packages, this does not really help much in a number of scenarios, e.g. when there are few packages to test (common in edit/test cycles), or when a package takes significantly longer to be tested than all others. ↩
Module count obtained on 2025-05-29 by searching on public Github repos using the search query language:"Go Module" /^go 1\.x/ where x is replaced by the go minor version (e.g. 24 for 1.24) ↩
Important
To clear any misunderstanding: this proposal aims at not changing the semantics of existing tests.
Proposal Details
#21214 proposed the same but was declined -- mostly because at the time we had no good way of rolling out such a change safely. As this is not the case anymore, and the benefits keep increasing with time (core counts are increasing, codebases and test suites are growing, etc.), I think it would be worth reconsidering it.
The idea would be to make parallel execution the default for modules that specify a go version (not toolchain version) higher than v1.X. Modules with go version below v1.X would still default to serial execution, as today. This would mean that behaviour would not change until a module go version (not toolchain version) is manually updated to v1.X or later.
Individual tests would be given a way to opt-out of parallel execution, similar to how today we allow individual tests to opt-in to parallel execution.
A one-off migration executed automatically when the module is upgraded to a go version after v1.X would ensure that existing semantics (serial execution) are maintained.
I encourage reviewers to focus on the goal when discussing this proposal, i.e. I welcome alternative ways/solutions to achieve the same goal, and will gladly update the proposal if a better solution is suggested. Some discussion is happening in https://gophers.slack.com/archives/C0VP8EF3R/p1747791714490489.
Proposal
testing.T.Serial()to allow individual tests to opt out of the new default. Mention that Serial was the default before go v1.X. No-op if the default is serial execution. Panics or fails the test if called afterParallel(). If present, it must be the first statement in the test function.testing.T.Parallel()to mention that this is the default starting with go v1.X. No-op if the default is parallel execution. Panics or fails the test if called afterSerial().go mod tidy(when it triggers the bump) orgo mod edit -go=1.Z1 from a v1.Y < v1.X to a version v1.Z >= v1.X, a one-off migration is performed2 on the module, addingSerial()calls to any test that does not have aParallel()call, and removingParallel()calls from any test that has it3.t.Parallel()calls as unneeded for modules with ago.modgo version equal or greater than v1.X.To guarantee the ordering of serial tests, the compiler should recognize test functions that call t.Serial() and arrange for them to be executed in the order in which they appear in the file. (This is currently the ugliest implementation detail of this proposal; alternatives are most welcome).
FAQ
Will I be forced to rewrite my tests to be runnable in parallel?
No.
A migration tool would automatically turn existing tests like:
into:
thus maintaining the current semantics, even after the migration to the new go version.
For cases in which
t.Parallel()is not the first statement with side effects in the function there are two options: either bail out of the migration and warn the user that manual migration is required (but this is pretty disruptive), or drop thet.Parallel()and as a fallback add at.Serial(), printing a warning about this (as it may slow the test suite).Users could then, over time, drop the t.Serial from tests that do not really need them.
Worth pointing out that, even without version control, this change is trivially reversible3.
Will this break my tests?
No. (see previous answer for details)
Why should tests be executed in parallel by default?
Because serial tests:
Parallel tests being the default nudge the whole ecosystem in a better direction, without forcing anyone to have to write parallel tests.
Isn't this going to be disruptive?
Statistics indicate that the vast majority of modules do not specify the most recent go version (1.24.*), or even just a go module version that was released in the last year.
This would seem to suggest that most existing modules will not be migrated to a go module version where the default is parallel execution. For modules that are migrated, the automated migration would make sure that existing semantics are maintained, therefore not requiring maintainers to spend any extra effort during the migration or later - unless they want to make use of parallel test execution (but this is orthogonal to the change, as it would be the case even if this proposal is not enacted).
I can acknowledge that some users that do not follow go development may be surprised by the new t.Serial() calls added by the migration tool, but I would suggest that a quick online search would almost certainly lead them to an article that explains the rationale.
All the above considered, I would argue that the change may be at most characterized as "surprising", but not really "disruptive".
Footnotes
We could detect also manual edits of go.mod if the module's go version was added to go.sum (or a different "lock" file). In this way the go tool could notice manual modifications of the go version, and either direct the user to run the migration using a dedicated command, or prompt the user that to continue the action a migration is required that will be executed if the user accepts. ↩
Whether the migration is performed silently, or whether the user is prompted to confirm they want to run the migration, is a UX implementation detail. ↩
This migration is trivially reversible even without version control by adding a
Parallel()call to any function withoutSerial()call, and removing allSerial()calls. ↩ ↩2This is further made relevant by the increasing core counts of modern infrastructure. Making use of the available cores to reduce test duration is much easier if most/all tests are executed in parallel. While it is true that
go testis normally already able to parallelize test execution across packages, this does not really help much in a number of scenarios, e.g. when there are few packages to test (common in edit/test cycles), or when a package takes significantly longer to be tested than all others. ↩Module count obtained on 2025-05-29 by searching on public Github repos using the search query
language:"Go Module" /^go 1\.x/wherexis replaced by the go minor version (e.g. 24 for 1.24) ↩