Lesson 1 of 17
Separation of Concerns
In this lesson
Roy Osherove separates automated builds (external link, opens in new tab), which are what is done, such as compilation and unit and integration tests, from continuous integration (external link, opens in new tab), which is when it is done, through triggers on CI servers (external link, opens in new tab). Build scripts (external link, opens in new tab) hold knowledge about the application and belong in its source control, while CI servers hold operational knowledge such as FTP servers and passwords and keep their configuration apart. With examples of a renamed test folder and a new FTP location, he shows how mixing the two forces changes in two places, and fixes the second by having the CI server pass the FTP name to the build script in an environment variable.
Transcript
[00:02] (external link, opens in new tab) First, let's talk about the difference between automated builds (external link, opens in new tab) and continuous integration (external link, opens in new tab). For a lot of people, there doesn't seem to be any difference. A lot of people think about those things as the same thing. I don't. I think about them as two different separate things and that makes a big difference in the way that I structure and automate my application and its deployment. The way I think about it is that automated builds are what I do. For example, unit tests, integration tests, compilation, etc. And continuous integration is when I do that.
[00:34] (external link, opens in new tab) For example, it would be the triggers. I do unit testing and compilation every time I check in the code etc. So continuous integration is usually a dumb trigger the way I think about it. And automated builds are the build scripts. So here for example we can say okay automated builds are all the scripts that I write using any kind of tool and continuous integration are the CI servers (external link, opens in new tab) the continuous integration servers that I might that I might used. So here's the way they work together.
[01:05] (external link, opens in new tab) A CI server might trigger a build script and for example in this case the build script can be of any kind of of tool. For example, a CI server can be Jenkins, TeamCity, Hudson, and a build script can be written in rake and MSBuild, SCons, Paver, whatever. And what's different about this is that if we see that there is that those roles are different, we can also separate concerns between those two different types of tools.
[01:40] (external link, opens in new tab) So the way I think about it is that CI servers are part of operational knowledge. They they have knowledge about the environment like FTP servers, passwords, locations. And build scripts have knowledge about the application. They're the development structure and I try not to mix those two different knowledge pieces of knowledge with each other. Because when I talk about application code and operation configuration, a lot of times an application's code and its structure may change many times a day, whereas operation configuration can change in different times of the day or in in a much slower pace.
[02:23] (external link, opens in new tab) And if we mix those two up, we would have to change one with the other. So what I would recommend is that when you have these two types of tools, you have CI servers and you have build scripts. I would recommend that the build scripts would actually be in our application source control. Whereas the CI servers are either not in source control or all their configuration is in a separate source control because it's basically a different type of application.
[02:54] (external link, opens in new tab) It changes on different basis. What could happen if you don't do this right? A lot of a lot of teams, a lot of projects actually don't have build scripts or they have the build scripts literally inside the CI server. So in my mind they integrate operational and development in the same module and that would create maintenance problems the or more maintenance than than needed.
[03:29] (external link, opens in new tab) So for example what happens when you mix up CI and the scripts together in the same location? Here's one case. So imagine that we in the CI server we have a script that runs a test in a folder called tests. Now that was in version one. Tomorrow I'm going to move to version two of my application. And when I move to version two I realize you know what I would rather separate the my test into slow and fast. So I have instead of a single test folder I would have a unit test folder and integration test folder.
[04:01] (external link, opens in new tab) Now, if I didn't consider things thoroughly enough or if I don't know everything that's running in the CI server, I might just re change the test the change the test folder and have unit test and integration test instead of it. I could also have knowledge about what the script is running in the CI server, but then that's the whole point. I want to decouple these pieces of knowledge. Now, because in this case, they're coupled together. when I change the folder name, I also have to go to the CI server and change the script in the CI server.
[04:34] (external link, opens in new tab) That causes me to change to have the knowledge in two locations. So, I'm repeating myself in many ways and it causes me to have two changes instead of one change. So in this case, coupling the application source structure to the operations knowledge forces me to change the operation knowledge on every simple application source change that is coupled. Let's look at at the opposite case. In this case, I might have a CI server and the CI server has
[05:08] (external link, opens in new tab) Is basically running a script for tests. This is a this is a this is how I would solve it. Basically, I have a CI server and I've solved it by having both by having the script that runs the tests inside this the application source control. So, in version one, the script knows about the structure of the code. So, it knows to run folder tests. In version two, the the folder has changed and I'm also changing the script, but it's all changed in one single location.
[05:39] (external link, opens in new tab) So, the source control actually the source knows about its own structure. the so the source knows how to build itself and this is a more maintainable way you keep everything in the same location. Here's another problematic structure. This case we couple the other way around. So let's say we have an old FTP on the top and we're moving to a new FTP. That's an operational piece of knowledge. The application shouldn't care where it gets deployed to.
[06:10] (external link, opens in new tab) It just needs to be deployed somewhere. But in this case the scripts are in the source control in the application but they also have coupling to the name of the FTP. So when the FTP name has to changes the application source control also has to change. So I actually need to create a new version sub version of even of minor to change the source control to the new FTP location. So again I have to repeat myself in two different places.
[06:41] (external link, opens in new tab) It would be much better if I could only change the name in one single place and then have everything just continue to work because I coupled operations knowledge to application source control then and then I forced myself to change the application source when I make simple operational changes. How would I solve that? Well, in this case, instead of having the build script, not having the it having it have knowledge of the name of the FTP, I will have the build script use an environment variable and have the CI server which invokes the build script simply set an environment variable to the new name of the FTP.
[07:20] (external link, opens in new tab) So then the name of the FTP only exists in one location and that is the CI server. And the build script is fully, how should I say it, almost stupid. it deploys to an FTP, it doesn't care where. It's think about it like dependency injection or inversion of control. Whereas the CI server tells the build script what to do. And the build script doesn't care where it goes. It just cares that it can do the right things. So this decoupling allows me to have better m maintainability and also keep all the knowledge, all the operation knowledge in a single place.