We have 10-30 changesets a day at Kinja and each changeset is immediately built and pushed live by Jenkins. Build time was ~4 minutes and deploy time was around ~6 minutes, meaning any changeset you pushed to Git could go live at most in 12 minutes. That's good already, but the question was always there if we could make it any better. Plus ~80% of our changesets were template or asset only, rendering full deploys with JVM restarts and warmups and all kind of an overkill. So here's what we did after some research.
Build process was tweaked so we don't clean the previously compiled bytecode if we don't have change in the Scala code. That alone is a great decrease in build time for the majority of builds. Closure templates and assets are now added to a zip file in build time and we copy this zip to every node when deploying, to a directory with the name of the actual Jenkins build number. Templates and assets are not served from the jar file used by Play! any more but from this versioned directory. We introduced ExternalAssetController for asset serving and we added a new API endpoint to our codebase bumping the build number and optionally reloading the Closure template rendering engine. Finally we changed our deploy script to check the changeset being built: whether we need a full deploy, or we had templates changed, or else we had assets changed only.
Every build is still a full build with either freshly compiled or inherited bytecode, so any successful build can be deployed to a newly started node. Otherwise, on non-full deploys we only have to copy our new zip to all the nodes and send a call to the API endpoint mentioned above. Also, with this API endpoint it became really easy to roll back a deploy if anything went wrong with the last one. Finally, we can get away without a JVM restart ~80% of our deploys, and that is a huge win: 80% of our changesets are now live in 2-6 minutes after a Git push, instead the prior 10-12 minutes.