I understand classic branching and deployment model as saying (in some small variant of the below):

  1. makes the changes on dev branch
  2. merge dev > staging branch, deploy to staging server, test it out
  3. merge dev > production branch, deploy production server.
  4. dont make commits directly on staging or production branches. Only merge changes into those branches from dev.

My issue is with point (4): I have some files that will be different depending on whether I'm running on dev, staging or production. These may include things such as robot.txt, server launch file (maybe staging is on Heroku and launch with a Procfile while production is on EC2), etc.. things that are deployment version specific and not application version specific.

The branching model above seems to say that right after a full deployment run, production = staging = dev. However, it does not leave space for those adjustment between the different environments.

How is this solved in industry?

===== solutions I can think of =======

  1. using environment variables on each env. Does the job for in code config switches. I already use this but I don't see how this can effect changes that are outside application code.

  2. adding deployment changes as commits directly on staging branch and production, and merging in application changes from dev (e.g. staging maintains a different version of robot.txt then production). Seems to contradict point (4)

  • 3
    staging is on Heroku […] while production is on EC2” – of course that's just an example, but isn't it problematic (and runs against the whole purpose of a staging environment) to use a radically different environment for staging than for production? I always though these should be as similar as possible in order to minimize surprises during deployment. Of course lower-fidelity environments are still fine for most kinds of testing.
    – amon
    Commented Nov 17, 2017 at 17:20
  • You're most likely right, I only just started looking at EC2 and EBS, so this was just an example off the top of my mind.
    – Jad S
    Commented Nov 17, 2017 at 17:26
  • 1
    Definitely do not deploy different environments from different branches. Build/package once from master, deploy from that single build. Make environment specific config part of the environment.
    – Ant P
    Commented Nov 20, 2017 at 20:25

1 Answer 1

  • Git is not suitable for managing different variants of your code.
  • Git is not a deployment mechanism.
  • Git is just a source code management system.

One thing that gets lost when using git push as a glorified FTP command is that source code is source code, and not necessarily the same thing you deploy. That even holds for interpreted systems where the source code can be executed directly.

One way to solve this is to introduce a build and deployment process that gathers the necessary files and deploys them to a certain target. Your source tree in Git might then be in a valid state for running it in a development mode, but for your production targets different config files will be used. For example, but not necessarily, a Docker image could be such a deployment format.

If a deployment target (such as Heroku) expects you to deploy via Git, it would be possible to use another repository or branch separate from your source code that contains the state to be deployed. This feels rather hackish, but actually works very well. However, this branch/repo does not contain any source code that needs to be version controlled since it's mostly a duplicate of the actual source code (from which it can be rebuilt at any time). It may therefore be occasionally desirable to prune these branches. You also lose a direct association between the deployed commit and the corresponding source code commit. Heroku may also offer other deployment types that do not abuse Git, but I'm not aware of those.

If a deployment target lets you run run pre-deployment scripts, that would be another solution. Such a script can then set up the necessary environment. For that to work, this script must be ignored by other targets or can detect under which target it is running.

In any case, adding deployment target-specific commits during your release process that are not expected to be merged back into dev is an antipattern. These will either get removed in a merge, or never become part of the history of other branches. Git is not good at maintaining a set of changes that only live on one branch if you want to merge other branches.

  • I totally get where you're coming from in saying deploying with git is abusing the tool. But I just switched from Heroku to Elastic Beanstalk and it turns out that's also used there. Can you give a link elaborating what you meant in your 3rd paragraph? I'm curious to get more detail on how that would work
    – Jad S
    Commented Nov 17, 2017 at 17:23
  • 1
    @JadS Branches of a Git repo can be checked out in multiple directories at the same time. E.g. you can check out a deploy-heroku branch in a build-heroku directory, where that directory is .gitignored otherwise. To do that, you would git worktree add build-heroku deploy-heroku (see this SO answer). You can then copy files into that directory, and commit+push within that directory to deploy. A similar technique based on write-tree and commit-tree is also possible which doesn't require you to check out a branch, but that's fairly deep Git magic.
    – amon
    Commented Nov 17, 2017 at 17:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.