How to Set Up Your Staging Environment for Web Applications
By: Simon Auer
Tags: DevOps, Deployment, Github, Git, Gitlab

How we deploy our applications and make sure that the final product is always up to our clients' standards, and the way we expect them to be!

Intro

I am a big fan of process optimization and I love trying out new ways to develop, deploy, and work on big software projects.

As head of a software development agency in Vienna, Austria, I was lucky enough to have worked on over 100 big web and mobile applications over the last 15 years with startups, international companies, and other amazing developers to try, fail and refine these processes over and over again.

This doesn’t mean that we have the perfect process now (if that even exists), but this is what currently works well for us and the way we work right now.

To get this out of the way: If you have feedback, suggestions on that, please share them with us in the comments! I would love to hear your thoughts and the approach that works well for you.

We use gitlab for continuous integration/deployment, but this should work with everything else as well.

TLDR:

We deploy our applications in 3 stages:

(we will call them environments from now on)

Development (only internally to test and review every feature)

Staging (for the client to test and review at each sprint end)

Production (final system for end-users)

This approach is not new, and we did certainly not invent it, so I don’t want to convince you, but tell you of our experience and why we chose it for us to maybe save you the experience yourself. ;)

How we did it in the past

Before we get started, I quickly want to tell you about our past experiences. Maybe the approach there works ok for you, maybe you even prefer it to our current way, but I will tell you why we decided to move away from that.

Deploying directly to production:

A long, long time ago, we used to code for a project. Everyone made sure their work was ok, and then we deployed the product (back then, probably just using rsync/FTP transfer ;) ) to the production server and hoped for the best.

The downside was: Many problems, and even though everyone was super careful, there was always trouble with dependencies, bugs, …

Deploying to test server right before release:

We also used the approach for a very long time to have one testing server in between that we deployed to test before we deployed regularly.

This worked quite well, and our software testers and product owners could revise the new features and check for bugs before it went online.

The problem was that we had to reschedule the production deployment too often because somebody noticed last-minute bugs and wrong implementations that were not working as expected.

That led to a lot of stress on the day before deployment and dirty hack solutions to fix them in time.

Deploying to a staging server regularly:

Our next approach was to deploy to the test server more regularly to review the changes as soon as possible and then release them at the end of our sprints.

That did not work out perfectly, but not because it was a bad practice, but because we often had the client looking or testing the application. It started to get confusing for the client and us both because he/she could never trust that the current code on testing is really stable.

We often asked our devs to “not deploy this week, because the client is testing,” which led to issues being held back and not being reviewed properly. That basically ended again in “Deploying to test server right before release.”

Also (at least for us right now), it was always hard to do thorough code reviews fast enough to make sure the developer could put the issue on “done” in time and QA/UX could recheck the features.

3 environment solution

This led us to the obvious solution:

Let’s divide our internal testing and client side staging server

Now we have the following environment setup:

Development (only internally to test and review every feature)

Staging (for the client to test and review at each sprint end)

Production (final system for end-users)

A quick overview of our deployment workflow:

As a basis for our deployment strategy and version controlling, we use Gitlab Flow :

Gitlab Flow with Release branches (image by GitLab)

This is what our process basically looks like:

Animated workflow (open the image with right-click to see it a little bigger)

Here it is again in non-animated form, to inspect in detail:

Our workflow for new features

Step 1 - Development # Use feature branches.

Every developer pushes their code features/bugfixes/… on dedicated feature branches that follow the naming convention

{TYPE}/{INITIALS}/{FEATURE_NAME}

{TYPE} = either bugfix/feature/hotfix

{INITALS} = 2–4 long name initials (to know who is responsible for branch)

{FEATURE_NAME} = task or feature name

Here is an example of a branch name for my new feature of “adding push notifications”.

feature/siau/adding-push-notifications

The code will be edited there, and everyone should try to push as often as possible.

The branch does not need to be in a fully working state at all times and is usually only used by the feature author.

Step 1 — development of the feature in the feature branch

Step 2 - Preview # Merge into development branch

When the feature (at least partially) works and needs to be tested (or when another dev needs to implement an interwoven feature with this one), the developer can merge their changes directly into development without waiting for approval or code review.

This will trigger an automated deployment to the dev server.

The dev server is only accessible internally and will be used for UX/UI reviews.

This is done to make sure there is no waiting time for reviewers to look at the code, to test on the implementation, and also to make it possible for our product testers, designers, UX people, frontend devs to check the live result before a code reviewer even spends time to check the code.

If something was found to improve or fix in this development version, feedback will be passed back to the developer in the form of a comment, and the dev will fix it in the feature branch and repeat this process until everyone is happy with the result.

Step 2 — merge into the development branch for review + feedback + fixing.

Step 3 - Staging # Merge into master branch

If the feature is finished and all the internal UI and product reviewers are happy, we create a pull/merge request into our master branch.

This pull request, however, is not merged automatically. It has to be code reviewed and accepted by at least one other developer before it is fully merged and deployed to the staging server.

Therefore, the master branch is always a representation of clean, valid, and checked UI and code.

Code Review:

If potential code improvements are found during code review, and changes are necessary, feedback is provided. The dev needs to incorporate them in the feature branch, push them to dev-branch first to see if everything works, and then push them again to the master branch for another code review.

If that turns out positive, the reviewer will merge it into master and trigger a deployment.

The reviewer will delete the feature branch on merge in.

Step 3 — Merge features into the master branch for code review and client testing

Client/Product owner improvement suggestions:

If new feature requests or improvements are planned, a new ticket will be generated, and we start again at step 1.

Step 4 - Release # Merge into the production branch

At the end of every sprint, we merge the staging branch into our production branch and thereby deploy all of our new features that have been tested on master/staging to our production server.

Step 4- Merge all of the masters into the production branch for release deployment.

That’s about it. :)

Thank you very much for taking the time to read my thoughts.

If you went this far down, I would really appreciate your comment and input.

How are you guys doing it?

I will post more about DevOps, deployment, coding (especially react, flutter and laravel) in the future, so follow me to read more ;)

Now try it out

If you think this could work for you as well, you can find my other blog posts about setting it up here:

Setup for PHP/Laravel projects using these environments:

Deploy PHP/Laravel applications using Gitlab CI/CD