A good article on the topic: Versioned migration of the database structure: basic approaches.
Features of database migration in case of canary releases: On the way to Canary (there is also a description of this type of releases).
Delivery of changes
Note: An alternative approach is described in the Docker chapter.
So, you have a civilized and centralized code. But what about laying out new functionality for end users? Shipping method may vary. For example, in the case of an intracorporate thick client (a la AWP), this can be laying out the installation package in a network folder, and in the case of a site, an archive with scripts. It is important to introduce the concept of a distribution kit – a complete set of software suitable for distribution. It is important that the distribution kit should not contain contour-dependent settings and specific configuration files. You should be able to “roll out” the once assembled distribution to any circuit, and with this installation immediately bring the system to the version fixed in the distribution, including the database migrations that we talked about earlier.
The distribution kit of the system should be formed on a separate machine, called the build server (buildserver). The reason is trivial: if the “combat” distribution is built on a specific developer’s computer, it can be very difficult to repeat this configuration in the event of some kind of breakdown or abnormal situation. You can also get into an uncomfortable position if the developer goes on vacation or quits.
Returning to the conversation about startups. There is such software GitKraken. I used it for a couple of months when I was in despair after the final slide of SourceTree into the abyss of bugs. So, I accidentally came across the installation package of this git client when I was practicing extracting information from an EXE. It turned out that the distribution kit is going to a specific user on the machine, in a folder a la C:\Users\Joey\Documents\…. And they looked technologically advanced and modern.
Let’s continue the theme of the buildserver. It should have an ideal environment installed, the necessary set of tools and settings to turn the source code into the final distribution. Build steps, rules, and commands are usually configured in the Jenkins, GitLab, or TeamCity class software. In a narrow sense, they can be called an assembly system. A special agent will need to be installed on the buildserver, allowing it to interact with the build system. The result of going through the assembly steps is usually the so-called. an artifact (or several artifacts), an example of such an artifact would be a distribution. It makes sense to store build artifacts outside the build system, such as in Nexus/Artifactory. This will help solve the issue of reliable storage of all versions of system assemblies.
System version
In the chapter about the delivery of changes, I want to additionally touch on the concept of a system version. The version format can be anything:
simple numeric, for example 7, 25, 144;
compound, for example 25.4, 33.1;
canonical (it is classical semantic), for example 7.1.33 (MAJOR.MINOR.PATCH);
custom, for example 7.1.33.eb4512a (commit hash added at the end).
Rule: The software receives a unique sequential version number when it is built. TeamCity, for example, has a special build counter, an automatically incrementing counter that is ideal for assigning unique numbers. It follows from the rule that:
there should not be different assemblies (distributions) with the same version number;
a later build must have a later version.
Additionally, I note that you should not introduce separate versions for the database, server side, thick client, etc. On the contrary, all components of the system that are in the distribution must have a single pass-through version number. This approach will help when installing system updates, making errors and logging incidents.
Automatic Deployment
So, now you have distributions created: by a button or automatically when merging into the master branch. You can, of course, install them on the target contours manually, but it would be more far-sighted to set up automatic installation. In fact, this is a certain set of instructions and commands that are executed sequentially. At the same time, it is important to remember that some commands require parameterization for the target contour (since the distribution kit is contour-independent). Implement this using TeamCity, GitLab, or even python scripts. I will consider the case of using Ansible / AWX.
I will quote one of the articles about Ansible on Habré:
Ansible is a software solution for remote configuration management. It allows you to configure remote machines. Its main difference from other similar systems is that Ansible uses the existing SSH infrastructure, while others (chef, puppet, etc.) require the installation of a special PKI environment.
Importantly, Ansible allows you to play some scripts on a remote host. A typical auto-installation scenario may include downloading a distribution, unpacking, stopping the service, parameterizing configuration files, copying files, running database migrations, restarting the service. Loop dependent variables can be organized using inventories, with one inventory per loop.
Package Manager
A small addition to the buildserver theme: for external plugins and libraries, it is convenient to use the package manager. For example pip for Python, nuget for C#, npm for Node.JS. Package manager configurations should be kept in source control along with the main system code. The build steps on the buildserver should include steps to restore and download packages according to the configuration file/files. It will also make it easier for a new developer to join the team – he will not need to configure his working environment in a special way, the package manager will take care of everything. The only negative is that some specimens sin with heaviness, but little can be done about this, they still bring more benefits.