Continuous integration (CI) and how it can help you
Continuous integration (CI) is a process by which we verify our project upon every change that occurs in the codebase. What exactly is the integration? It depends on how you configure the process: it can be as simple as installing the dependencies and compiling the project or as complicated as running many different scripts to determine whether the codebase is in an acceptable state.
You can think about CI as a diligent coworker who is always there, waiting to double-check your changes before merging them to the main branch. It’s a good idea to include merge requests in your workflow when CI is in place, even if you work alone on the project. Your changes will be reviewed by the machine, and leaving them on a separate branch allows you to fix any issues before merging to the main branch.
Without CI, each and every developer is responsible for verifying all their own changes. Of course, from time to time someone will forget—maybe the original commit was OK, but what if after a rebase or merge it has an issue? Without CI, you let your least careful colleagues push and forget about their changes, and others are forced to clean up after them.
How CI is structured
Continuous Integration checks your commits. For every code change, CI usually runs a few different tasks in a defined order. You can use the output of one job as an input in another; for example, you can build an application in one step and then use the resulting package for testing. Usually, you manage CI with a configuration file that sits inside the repository—thus, your CI can evolve together with your codebase.
If all the tasks pass, then the commit is passing; if any of them fails, then the commit is failing. In an ideal situation, the commit content alone determines the CI result: it does not depend on external services, and there is no random element that could make it fail.
For each branch, CI shows the result of the topmost commit. The main branch should be almost always passing; any issue on it will affect everybody on the team, so fixing it should be a priority if some regression happens. For a feature branch, you should merge it only if it’s passing the CI.
Tasks you can delegate to your CI
You can set up whatever scripts you run on your local environment on CI. The list can get long in big projects, but let’s take a look at the CI tasks you can expect in projects of any size.
The most basic check you can perform on your codebase: does it compile? It’s a step that will catch any dependency that was installed but not saved, any typescript type mismatch that sneaked into the commit. These are easy fixes while the developer is on the task, but those errors can become confusing or annoying if shared with others.
Static analysis involves checking your code without running it. On frontend projects, you can often see tools such as:
Those programs are the most helpful when integrated with the code editor. Running them on CI is an additional check that can help you in two ways:
- it will identify any developer who forgets running them locally—so they can be asked to do so before they mess up a lot of code
- it will identify any version or configuration mismatch that could occur between the different development environments
Having a CI in place and running tests on it are essential if you’re serious about automated tests in your application. The whole point of automated tests is to run them very often—what better moment to do it than when some code changes are about to become public? Not doing so is an invitation for a scenario in which:
- one developer introduces regression into the codebase
- others add unrelated changes on top of it
- someone finally runs the tests that catch the original regression
- they waste time troubleshooting issues they didn’t cause related to changes they are potentially unaware of
In this scenario, the main issue is that as you troubleshoot, you don’t even know when the issue was introduced; it could be in a previous commit, or a week ago. You could
git blame or
git bisect your way out of it, but it’s way easier to simply know the point at which the tests stopped working.
Let me stress another thing: writing tests is an investment in quality assurance. It is an effort on a day-to-day basis. If you are putting in this daily effort, it makes sense to spend time, just once, setting up CI to get the most out of the tests you develop.
You often see CI together with continuous deployment (CD), abbreviated together to CI/CD. This is because as you compile and verify your code, you have everything ready for deploying—at least to the testing server. A true CD would call on you to deliver to production, but this can be more challenging, especially as it exposes the users of the project to potential regressions.
What are the downsides to CI?
The setup can be time-consuming, especially if you have never done it before. Even the most straightforward changes to the configuration can take a considerable time to verify, as you need to run it on an external server to which you don’t have direct access.
Dependency on an external provider
If you integrate Ci into your workflow, you will depend on your CI provider. If they are down, you cannot merge—at least not with all the safety net you are used to. It can be frustrating, especially if it happens somewhat often.
Many CI providers have a free plan that should be more than enough for simple exercises or demo projects. For a project where people are working full time, it’s almost certain that you’ll need a paid plan plus additional time for the CI machines to run your scripts. The cost will likely be worth it, even if you assume the CI saves only a few minutes per day for each developer in your team.
How about you?
Are you interested in learning more about setting up CI? I’m thinking about writing some more detailed posts about the setup of CI tools. By knowing which tool you are most interested in, I can create content that matches your expectations. So please, vote in the poll below! ⬇️⬇️⬇️ Your opinion is very important to me. Thanks!
To get even more value from your CI, you run end-to-end tests (E2E) on it. Setting up E2E on CI is a challenge, and I’ll cover it in another article. In the meantime, you can check out how to start with E2E.