The Drupal core team releases new versions of Drupal frequently. These can contain bug fixes, security patches, or both (sometimes, they even break the law and add new features). Keeping your websites up to date will ensure your users do not see unexpected errors produced by core, and helps prevent hackers from hijacking your site. Upgrading Drupal core in a live website with a few contributed modules is a pretty straightforward process. Doing it in a website with custom modules, large amounts of data, and custom business logic is no easy task. Patience and meticulousness are your best friends in this endeavor. Every website is different and most probably you will need to perform additional tasks when upgrading your Drupal site. At Lullabot we have come to a list of common steps which act as a guideline to reduce risks. Before we start with it, let's imagine the following (and pretty safe base to work from), scenario:

You have a production environment, a development environment and a local environment.

Your local environment has Drush installed.

You use a Version Control System such as Git to track code changes.

You are able to extract a database backup of your production environment.

You have SSH access to the Development and Production environments, plus permissions to manage the directory where Drupal is installed in each of these environments.

The local and development environments are useful, because they allow you to test upgrades before performing them on your production environment. If there's any way to avoid it, do not upgrade core straight in production (That would definitely qualify as "Extreme Programming.") The closest you can get to the scenario listed above, the better. It will help you trap bugs during the process before getting to production.

Verifying current and target versions

Let's start by checking how many versions behind we are (the higher the number, the longest each forthcoming step it will be).

Locally, run drush status to check what version the site is at. Go to http://drupal.org/node/3060/release, filter by the version of Drupal core you are using and see how many versions behind your site is. Read each of the release notes (do not be sneaky here, open the whole release node and do not read just the summary) to pinpoint API changes that may break the site (normally this is not the case, but beware). The Drupal core team makes it very clear if there is a change in an API (for example, a function have been removed or its arguments have changed). If that happens, verify that your custom modules (and maybe even contributed ones) comply with that change.

Updating the code locally and inspecting changes

Now let's get the new version in place locally:

Open a console and go to the root directory of our local Drupal installation Make sure that your code and database are up to date. The former may mean to execute git pull if you are using Git, while the latter can be achieved by executing drush sql-sync @myprodsite @self. Alternatively, you can just extract a database dump of your production environment, recreate your local database and load that dump into it. On the command line execute drush pm-update drupal to obtain the new release and update the database. Now you have the new version of Drupal core in your local environment. You may like to check what has changed in case you want to restore, for example, your customized .htaccess file, or in case you do not want files such as INSTALL.txt or LICENSE.txt in your root directory. You can get an overview of these changes with git status and quickly revert changes in some of the files with git checkout path-to-a-file. Now we are going to create a commit with the changes in core, while reading at what has changed. If you feel confident enough, you can just do git add . and commit that. Alternatively, if you want to really know what has changed in this new core release run git add --patch, which will go change by change and will let you decide if you want to commit or discard each of them. Note that this can be a very long process, but it will teach you a lot too.

Testing

Test the new release locally before pushing your changes to the remote repository. Navigate through your site and simulate the most important tasks in it, verifying that there is nothing that break them. If there are automated tests (the best and rarest scenario), run them. Next step is to push our changes to the remote repository and update the development environment. Normally you will just need to do the following (unless an automated job does it for you):

Log into the develop environment and install a copy of the production environment's database. Go to the Drupal root directory. Run the following commands: git pull drush updatedb

That's it. Now let your QA team have a look at the site for a while. If your workflow follows a SCRUM methodology or similar, try to get the core upgrade into the development environment at the start of a sprint so the rest of the team can test the new codebase while the sprint goes on.

Hitting the red button to go to production

Once you have done enough testing, you are ready to go. The steps would be pretty similar to the ones in the previous section, except that you should already have backups of the current and previous states of the production's database. It is very useful to use git tags for the production environment and point it to them instead of a branch, as it gives you the option to roll back to the previous tag in case something goes wrong.