From the course: Continuous Integration and Continuous Delivery with GitLab

Continuous integration

- Let's talk about CI, also known as continuous integration. I like to think of this concept as just what the name implies. Integration, in this case, the integration of changes into your code base. Integration that is continuous because it's being done in an automated system on demand. That might sound like a circular definition. So it might be easier to understand if we think about the alternative. In CI, ideally you merge right after the changes are complete. The alternative is periodically having some kind of merge event at a predetermined time where some poor developer is tasked with combining all of the changes that were created by the other developers that sprint. In CI, you need to QA every change as soon as it's ready to merge. This requirement means that manual QA before merge is pretty much out of the question. The continuous part of CI requires that you have automated tests. This means that it's pretty easy to figure out what code change triggered a bug. And you can even require that the test pass before code can be merged. In non-continuous integration, QA happens once all the changes have been merged. That way you can test how everything interacts and find any bugs. If you think about it, this is probably more efficient in some ways, especially if QA involves a lot of manual work. The problem is, any bugs are much harder to track down because of how many code changes have happened. In CI workflows, there's no inherent deadline to the process. It's much more driven by when the work is ready. Obviously deadlines still exist. But in a CI workflow, often those changes that happen right before the deadline are minor. Last minute bug fixes and small cleanup changes. The major changes have been merged over the course of the sprint as the features were developed. The non continuous way of doing things is heavily calendar and deadline driven. All code changes need to be ready to merge by a certain date. Because the change in code is so big, it's pretty much scheduling a crisis for every release. If you've ever seen a documentary about archeology, there's a good chance you saw them sifting through dirt on an archeological dig. That sifting process is a good metaphor for how to think of automated testing. Each layer of testing is like a finer mesh sieve that catches smaller particles. They shake that dirt through a big sieve first and pull out the larger things like rocks to throw away and pieces of pottery to save. Then the medium size holes catch the next level of artifacts. Eventually, the entire team can gather around the tiny fine mesh to catch things like individual beads and tiny fragments of pottery and let the unwanted dirt and dust pass through. The main point of this metaphor is that you wouldn't start sifting with the fine mesh. It would take forever and would probably get damaged by the big rocks. It's the same with testing. You want to have your most expensive tests in terms of time or computing power be last. Catch the big, easy stuff first and then move on. This isn't a course on automated testing. So I'll be pretty general here. I like to break down testing into three or four categories. First is syntax testing, which is just making sure your code is actually valid in the language you're using. Linting is similar. It's using a tool designed for the language you're using to enforce a particular style. I call that one level because they're functionally similar. Just testing the text of your code. The next level is unit testing. I also lump in integration testing here, but you could make that the level below. Unit testing focuses on individual units of code. For example, testing a function with various valid and invalid arguments and comparing that to the expected output. Integration tests are tests that focus on a larger scope. Does this feature work as expected? Does the API call return the expected output? Et cetera. Acceptance testing is the fine mesh sieve of testing. These might be similar or identical to your integration tests, but the important difference is that they're run in an environment that is as similar to production as possible and simulating real user behavior. Acceptance testing is all about simulating the real world scenarios that your code might encounter. The reason that you want to have these separate levels is that your tests should happen in a pipeline. If there's a syntax error in your code, it would be nice to find out within a few seconds of submitting your merge request instead of waiting several minutes for your entire suite of tests to run. Your tests should fail early and fail often. That is they should quit as soon as something goes wrong so that you can quickly fix the issue and retest. They should fail often, in that, it's often more efficient to just run your tests than to meticulously look over your code for bugs before submitting a merge request. This also means that you need to put time and effort into comprehensive tests. But that investment will pay itself back many times over when it enables you to develop more quickly and to trust that the code is good. I'd also like to add that writing code with a good solid CI system and test is just more fun. It takes away a lot of the worry that some change will break things, which is very freeing.

Contents