Step 11: Branching the Code
Branching the Code¶
There are many ways to organize the workflow of code changes in a project. But working directly on the Git master branch and deploying directly to production without testing is probably not the best one.
Testing is not just about unit or functional tests, it is also about checking the application behavior with production data. If you or your stakeholders can browse the application exactly as it will be deployed to end users, this becomes a huge advantage and allows you to deploy with confidence. It is especially powerful when non-technical people can validate new features.
We will continue doing all the work in the Git master branch in the next steps for simplicity sake and to avoid repeating ourselves, but let’s see how this could work better.
Adopting a Git Workflow¶
One possible workflow is to create one branch per new feature or bug fix. It is simple and efficient.
Describing your Infrastructure¶
You might not have realized it yet, but having the infrastructure stored in files alongside of the code helps a lot. Docker and SymfonyCloud use configuration files to describe the project infrastructure. When a new feature needs an additional service, the code changes and the infrastructure changes are part of the same patch.
The workflow starts with the creation of a Git branch:
$ git branch -D sessions-in-redis || true
$ git checkout -b sessions-in-redis
This command creates a
sessions-in-redis branch from the
It “forks” the code and the infrastructure configuration.
Storing Sessions in Redis¶
As you might have guessed from the branch name, we want to switch session storage from the filesystem to a Redis store.
The needed steps to make it a reality are typical:
- Create a Git branch;
- Update the Symfony configuration if needed;
- Write and/or update some code if needed;
- Update the PHP configuration (add the Redis PHP extension);
- Update the infrastructure on Docker and SymfonyCloud (add the Redis service);
- Test locally;
- Test remotely;
- Merge the branch to master;
- Deploy to production;
- Delete the branch.
All changes needed for 2 to 5 can be done in one patch:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
--- a/.symfony.cloud.yaml +++ b/.symfony.cloud.yaml @@ -4,6 +4,7 @@ type: php:7.4 runtime: extensions: + - redis - pdo_pgsql - apcu - mbstring @@ -24,6 +25,7 @@ disk: 512 relationships: database: "db:postgresql" + redis: "rediscache:redis" web: locations: --- a/.symfony/services.yaml +++ b/.symfony/services.yaml @@ -2,3 +2,6 @@ db: type: postgresql:13 disk: 1024 size: S + +rediscache: + type: redis:5.0 --- a/config/packages/framework.yaml +++ b/config/packages/framework.yaml @@ -7,7 +7,7 @@ framework: # Enables session support. Note that the session will ONLY be started if you read or write from it. # Remove or comment this section to explicitly disable session support. session: - handler_id: null + handler_id: '%env(REDIS_URL)%' cookie_secure: auto cookie_samesite: lax --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -8,3 +8,7 @@ services: POSTGRES_PASSWORD: main POSTGRES_DB: main ports:  + + redis: + image: redis:5-alpine + ports: 
Isn’t it beautiful?
“Reboot” Docker to start the Redis service:
$ docker-compose stop $ docker-compose up -d
I’ll let you test locally by browsing the website. As there are no visual changes and because we are not using sessions yet, everything should still work as before.
Deploying a Branch¶
Before deploying to production, we should test the branch on the same
infrastructure as the production one. We should also validate that everything
works fine for the Symfony
prod environment (the local website used the
First, make sure to commit your changes to the new branch:
$ git add . $ git commit -m'Configure redis sessions'
Now, let’s create a SymfonyCloud environment based on the Git branch:
$ symfony env:delete sessions-in-redis --no-interaction
$ symfony env:create
This command creates a new environment as follows:
- The branch inherits the code and infrastructure from the current Git branch
- The data come from the master (aka production) environment by taking a consistent snapshot of all service data, including files (user uploaded files for instance) and databases;
- A new dedicated cluster is created to deploy the code, the data, and the infrastructure.
As the deployment follows the same steps as deploying to production, database migrations will also be executed. This is a great way to validate that the migrations work with production data.
master environments are very similar to the
master one except
for some small differences: for instance, emails are not sent by default.
Once the deployment is finished, open the new branch in a browser:
$ symfony open:remote
Note that all SymfonyCloud commands work on the current Git branch. This
command opens the deployed URL for the
sessions-in-redis branch; the URL
will look like
Test the website on this new environment, you should see all the data that you created in the master environment.
If you add more conferences on the
master environment, they won’t show up
sessions-in-redis environment and vice-versa. The environments are
independent and isolated.
If the code evolves on master, you can always rebase the Git branch and deploy the updated version, resolving the conflicts for both the code and the infrastructure.
You can even synchronize the data from master back to the
$ symfony env:sync
Debugging Production Deployments before Deploying¶
By default, all SymfonyCloud environments use the same settings as the
prod environment (aka the Symfony
prod environment). This
allows you to test the application in real-life conditions. It gives you the
feeling of developing and testing directly on production servers, but without
the risks associated with it. This reminds me of the good old days when we were
deploying via FTP.
In case of a problem, you might want to switch to the
$ symfony env:debug
When done, move back to production settings:
$ symfony env:debug --off
Never enable the
dev environment and never enable the Symfony
Profiler on the
master branch; it would make your application really
slow and open a lot of serious security vulnerabilities.
Testing Production Deployments before Deploying¶
Having access to the upcoming version of the website with production data opens up a lot of opportunities: from visual regression testing to performance testing. Blackfire is the perfect tool for the job.
Refer to the step about “Performance” to learn more about how you can use Blackfire to test your code before deploying.
Merging to Production¶
When you are satisfied with the branch changes, merge the code and the infrastructure back to the Git master branch:
$ git checkout master $ git merge sessions-in-redis
$ symfony deploy
When deploying, only the code and infrastructure changes are pushed to SymfonyCloud; the data are not affected in any way.
Finally, clean up by removing the Git branch and the SymfonyCloud environment:
$ git branch -d sessions-in-redis $ symfony env:delete --env=sessions-in-redis --no-interaction
This work, including the code samples, is licensed under a Creative Commons BY-NC-SA 4.0 license.