Deployment of PHP Applications

by Mike Willbanks on October 7th, 2009

We’ve all been there, working through how we are going to be deploying the next latest and greatest solution for our company. Going through all of the rigger just to think, hey wait a second, how are we going to handle the deployment process and integrating our current solution with what we have ready for a release. Release management is always simple but yet so complex.

This all starts off with versioning, if you are not versioning your application, you have bigger problems to solve. Stop reading and get on to working with Git or SVN; If you think that it is too small for versioning, crawl back in your hole and do everyone a favor and quit the profession (not really, but really). Versioning is the main key to your deployment, you need to be able to version everything, utilize branches for major features and be fully confident in that you will not be dropping productivity in the work schedule due to the deployment in a few days.

The next item on the critical path is having an adequate bug tracker, you need to ensure that you have a solution that supports milestones or releases. These help you to match up the tickets with the release that you will be pushing out the code for. When you make a commit, ensure that it is matched to a ticket in this system as it will be very important later.

Let us start off with the version control structure, in which, we will utilize the standard structure: /trunk, /branches, /tags. In your trunk you will essentially commit anything such as bug fixes, minor features and anything that is not going to take a long time or cause issues in relation to the current code base. Branches will in turn handle your release branch, major features and/or major architectual changes. This may or may not be how you handle it, but I feel that the release branch has to come out of a separate area than the trunk and have the ability to be merged (take it or leave it).

Everything in the release branch must come from the trunk, the trunk is going to be your latest development copy and/or integration copy. You should have an environment that has continuous integration, the current copy on a machine and is controlled through your post-commit hook. In order to merge to your release branch, you should setup a script to do this automatically when it comes to release time and ensure you have a test environment.

In my current environment I do this through the svn merge-info. This process works as such:

  1. Create a Hash Map of Available Revisions
  2. Look at the Commit Log and Mark Associated Tickets
  3. Look up Tickets from the Release
  4. Merge Revisions to the Release Branch

From here the environment is updated, we also utilize items such as LiquiBase and DBDeploy to handle our database versioning depending on the system that is being utilized. This ensures that we are handing the migration to the current version of the software. By this, we have a pre-deployment file as well as a post-deployment file (depending on the system, however, it is always implemented in this way). From here we run our system through functional testing and unit testing.

If anything is to be fixed, we apply it in the trunk and follow the merging process again before our deployment to our test environment. All of these deployments are handled through svn switch for our lower environments. You will not want to do this when you are in a production environment.

Once the release has been verified to being correct by a QA staff or whomever is in charge of verifying a release, a tagging operation takes place to ensure that we have a mark at the specific location if we ever would have to go back to a prior release. This is where the operations can be a little more crazy. When deploying to a server farm, you need to ensure that you have handled the pre-deployment and post-deployment adequately in order to provide your users the best experience. If you deploy database changes that the applications code was not ready for, you have just provided your users with a failure of your system.

Apply your pre-deployment database changes and/or system changes, then follow up by processing each web server. Do an export of the changes (so that there is not revisioning information) into a release directory. Your can either move the current release to a tag (which has more performance) or create a symlink to the current release. After these changes have been made apply the post-release database or system changes.

You can automate these deployments through Phing, Ant, or a custom script. It is all up to you. I’ve done it through a custom script rather than Ant. This is due to specific requirements, you might find that different departments would rather have a separate process due to their involvement.

While this process will not work for everyone, I believe it will help others that have not gone through the release process before. There is additional aspects to how people deal with releases such as creating a branch per release and then tagging to that branch, however, it is my feeling that all releases need to be merged to the trunk and the trunk merged to the release branch. I presently utilize a single release branch instead of multiple for the best quality and the probability of conflict resolution. If anyone would like me to elaborate further let me know as this is a far different subject than I usually post.

Edit: Included links to software mentioned.

From PHP

  1. Dear Mike,

    thanks for this great post.

    Especially LiquiBase and DBDeploy where some nice new hints for me to optimize our/mine deployment process.

    Greetings from Germany

  2. Maarten permalink

    Great article, if only for pointing me to liquibase and dbdeploy.
    What do you use for bugtracking? We’re using redmine, but I don’t think we can do the branching project track stuff with that…

  3. Maarten – Your bug tracker shouldn’t cause any hiccups with branching, the branching is on the version control level and if I remember right, redmine can track that with your pre-commit / post-commit hooks. Currently we are using a mixture of Trac and JIRA while we are converting over to utilizing JIRA.

  4. DBDeploy is a great resource, even if only for the fact that Phing ships with a DBDeploy plugin built straight in. It is pretty close to the metal though (deltas are SQL files partitioned into your UP and DOWN scripts).

    If you decide to use it, the Doctrine ORM has very nice migration features like being able to run a diff against your schema changes and last built models to create your migration deltas.

  5. Good article — I also enjoyed your ZendCon UnCon session on the topic. However, there is one piece that I’m still a little confused about. What is the purpose of having separate pre-deployment and post-deployment files? And, how exactly do you implement these two separate files? I think the purpose of the pre-deployment file is to make any database changes that are both backwards and forwards compatible (i.e. will run against either the old or the newly deployed application) and the purpose of the post-deployment file is to make any necessary database changes that may *not* be backwards compatible and so can only be run after deployment. Is this correct? Also, in your ZendCon UnCon session I thought you said that if you were using dbeploy you would have separate pre and post metadata tables. Can you go into more detail about how this would work? It seems odd to have two metatdata tables that are both responsibility for deploying the same database. Is the goal of all these pre and post files to have zero downtime due to database migrations or is there another benefit to this approach?

  6. Bradley – you are correct on both fronts, the main reason for having a pre and post deployment is to eliminate any potential downtime as well as having database changes that are capable for handling the current code base as you hit every server in an area.

    The biggest advantage that this has is that you can create the table structure for the current code base and then move it into what you need in the post.

    For example: We need to add a new column to our user table, there are a few ways to do it. Typically I would add a column in the pre-deployment where I want it to have a default and then in the post taking away the default as well as migrating the additional user records.

    Another example: We are moving to another table and it doesn’t make sense to make the changes directly to the table, we make a new table that the new application will utilize when the application is deployed per server, then we migrate the existing data to the new app post-deployment. For example, say you have a large table (1+ million records) you are going to take downtime if you modify it directly in relation to a commonly utilize table, however, if you utilize this strategy this is mitigated.

Trackbacks & Pingbacks

  1. Linktipps #15 :: Blackflash
  2. Mike Willbanks’ Blog: Deployment of PHP Applications | Webs Developer

Leave a Reply

Note: XHTML is allowed. Your email address will never be published.

Subscribe to this comment feed via RSS