Order of drush commands for automated deployment?
Drush 10.3.0 now provides a deploy command to standardize how Drupal deployment works. This command performs the following:

drush updatedb --no-cache-clear
drush cache:rebuild
drush config:import
drush cache:rebuild
drush deploy:hook


Database updates always come before configuration import! Support for automatic entity updates has been removed from Drupal core (change record) and entup has been removed from Drush core. Whenever an entity type or field storage definition needs to be created, changed or deleted, it has to be done via hook_update_N().

Here is the updated deployment routine I'm happiest with.
drush state:set system.maintenance_mode 1
drush cache:rebuild
git pull
composer install --no-dev
drush cache:rebuild
drush deploy
drush state:set system.maintenance_mode 0
drush cache:rebuild


This also means you have to run two releases if you want to uninstall and remove a contrib module. First release to deploy the updated config that disables the module. Second release to deploy the updated composer.json and lock file after you removed it with Composer.
To make real consecutive releases possible you might want to pass the current release's commit SHA1 to the deployment script and then replace git pull with a more exact routine (where $1 is the SHA1):
# If not empty 1st argument passed to the script, do:
if [ -n &quot;$1&quot; ]; then
  git reset --hard &quot;$1&quot;
else
  git pull
fi

Otherwise the consecutiveness can not be guaranteed when you push two new releases at once or within a short period. As then the first release's triggered git pull will simply pull the latest changes (from the second release), where instead it should pull only the changes included in the first release. See the full sample repo leymannx/drupal-circleci-behat.
Credit for this git snippet goes to CircleCI. This is how they are doing it in their containers.