Best practices for 2.6.x migration

225 views
Skip to first unread message

Will Sargent

unread,
Sep 12, 2016, 3:26:07 PM9/12/16
to play-fram...@googlegroups.com
Hi all,

So as we're getting into 2.6.x development in depth, I'd like to go over some best practices for migration where there are changes to existing APIs or addition of new APIs.

The first best practice is to document any migration.

Add the change in Migration26.md:


If it's a large change, then consider breaking out the documentation into its own page linked from Migration26.md, but note that in that case you must also add it to the table of contents:


Please see the documentation overview page at 


for how to run the documentation server and add validation and tests.

If you have a method or class that you would like people not to use any more, then first consider deprecating the class, and mark it as "deprecated in 2.6.0" so it's clear to other developers this is a new deprecation.  The deprecation message should contain both an indication of what to do about it, and a link to the migration notes explaining the migration in more detail:

@deprecated("Please use CompletionStage.acceptEitherAsync, see https://playframework.com/documentation/2.5.x/JavaMigration25", "2.5.0")
For example, play.api.libs.concurrent.Execution.Implicits.defaultContext depends on Akka.system, which is the execution context connected to Play.current's ActorSystem.  In this case, we want people to move to

class MyService @Inject()(implicit ec: ExecutionContext) {

}

but there is no direct documentation from the "old way" to the "new way" -- deprecation with a link to the migration notes is the best way to make that connection obvious to users.

Same thing goes for configuration.  play.api.Configuration has getDeprecated and getDeprecatedWithFallback for when configuration has moved -- in some cases (especially when Play depends on an underlying library that has changed settings, such as AsyncHttpClient), there is nothing for it but to indicate that the configuration is no longer valid and throw an exception -- see https://www.playframework.com/documentation/2.5.x/Migration25#play-ws-upgrades-to-asynchttpclient-2 for an example.

The second best practice is to avoid breaking anything where possible, and to provide a fallback approach where it is not.  

If you are dealing with something like implicit conversion in a base class or a change in execution flow which does not show up quite as easily in deprecation, then consider moving the deprecated methods into a DeprecatedImplicitConversions trait, and then that gives people something to fall back on.

The third best practice is to road test.  

Take an existing Play 2.5.0 project that uses the old API, and try changing it to the new API.  Assume the user changes the version number and doesn't read the documentation first -- what do they see first of all?  If there are any configuration settings that have changed, do they know how and where they have moved?  What would it look like to a first time user of Play?  Modelling the user experience of upgrading ensures that things go smoothly on launch.

Please contribute your own tips and feedback to this thread -- I think that the 2.5.0 upgrade went pretty smoothly overall for most people, but I'm always interested in hearing what worked and what didn't.

Thanks,

--
Will Sargent
Engineer, Lightbend, Inc.

Christian Schmitt

unread,
Sep 12, 2016, 4:46:09 PM9/12/16
to Play framework dev
Not strictly to Migration of 2.6.x:

Actually what didn't on our side (envisia GmbH) was adding newMethods to an Object in a minor version that mostly was imported with _ (https://www.playframework.com/documentation/2.5.x/ScalaForms#Imports).
I'm talking about the Forms object where we added Forms._ in 2.5.5 (https://github.com/playframework/playframework/pull/6249)
We should probably avoid backporting such things in the Future, even when it's not binary incompat it's still akward.

--

Now two things about Migration, I think that we may revisit the whole situation in Play, we should make shorter release cycles and keep one Version up to date for ~4 years. My company doesn't have this problem since we are using green Builds from master. However other people probably can't upgrade to the new version every year. So having a more supported version every 4 Years while having short lived version every 1/3 year (where we only support the current) would probablly make it more comfortable for "boring" businesses to use Play!

I mean currently we support 2.3.x with Security and 2.4.x and supporting 2.5.x with Backports. But actually I would prefer having such a schema:
- 1 Version (LTS) will get Security Fixes and other non breaking Change Fixes - 4 Years
- 1 Version (Current) will get Backports (if possible) - 1/3 Year
- master branch

I know this is probably more work supporting the "really" old version, but I guess it would make people way more happy, especially the 2.3.x -> 2.4.x -> 2.5.x was no fun for many people.

My companies "new" main product started with Play 2.4.x with Guice so we didn't had any breaking change at all, and other stuff gets updated regular we have like ~4 projects against play and started with 2.1, the biggest change I've seen in the whole year using was mostly the Result changes in 2.3.x As we were still quite early in the Scala game there it was quite a big hit for us. But after that we kept up with Play! And since we didn't used objects all over the place the other projects weren't hit badly by the DI change, but as said we are a small company and keep our stuff up to date and are very very careful when to use a library. 
We learned it the hard way on our Web Client stuff were the dependency management killed a lot of time.


Regarding the Road Test I couldn't find anything that didn't just work against play 2.6.x, however Joda Time Users will probably have a harder time.
Also Typed Keys are just amazing.
Reply all
Reply to author
Forward
0 new messages