Preparing your Code
In this section, we’ll cover certain aspects of your code and how they could impact the deployment of your software. Such software issues could include code not compiling (broken builds), avoiding relative path names, and making sure you wrote proper unit tests. These are a couple of the common errors I’ve experienced over the years; in this section, I’ll also provide solutions on how to fix them.
Before we review a CI pipeline, there are a few caveats we should address beforehand. Even though we covered a lot in the previous chapter regarding version control, your code needs to be in a certain state to achieve “one-button” builds.
In the following sections, you’ll learn how to prepare your code so that it’s “CI/CD-ready” and examine the problems you could experience when deploying your software and how to avoid them.
Building Flawlessly
If a new person is hired and starts immediately, you want them to hit the ground running and begin developing software without delay. This means being able to point them to a repository and pull the code so that you can immediately run the code with minimal setup.
I say “minimal setup” because there may be permissions involved to gain access to certain resources in the company so that they can be run locally.
Nevertheless, the code should be in a runnable state, send you to a simple screen of some kind, and notify the user to follow up on a permissions issue or provide some notification to resolve the problem.
In the previous chapter, we mentioned how the code should compile at all times. This means the following:
- The code should always compile after a clone or checkout
- Unit tests should be included with the build, not in separate projects
- Your commit messages to version control should be meaningful (they may be used for Release Notes)
These standards allow your pipeline to fall into the pit of success. They help you create a build even faster and easier when your code is in a clean state.
Avoiding Relative Path Names with File-based Operations
One of the troublesome issues I’ve seen over the years when it comes to web applications is how files are accessed in a web application.
I’ve also seen file-based operations through a web page, where files were moved using relative paths and it went wrong. It involved deleting directories and it didn’t end well.
For example, let’s say you had a relative path to an image, as follows:
../images/myimage.jpg
Now, let’s say you’re sitting on a web page, such as https://localhost/kitchen/chairs
.
If you went back one directory, you’d be in the kitchen with a missing image, not at the root of the website. According to your relative path, you’re looking for an image directory at https://localhost/kitchen/images/myimage.jpg
.
To make matters worse, if you’re using custom routing, this may not even be the normal path, and who knows where it’s looking for the image.
The best approach when preparing your code is to use a single slash (/
) at the beginning of your URL since it’s considered “absolute:”
/images/myimage.jpg
This makes it easier to navigate to the root when you’re locating files on a website, regardless of what environment you’re in. It doesn’t matter if you are on https://www.myfakewebsite.com/ or http://localhost/, the root is the root, and you’ll always find your files when using a single slash at the beginning of your sources.
Confirming that your Unit Tests are Unit Tests
Tests in your code are created to provide checks and balances so that your code works as expected. Each test needs to be examined carefully to confirm it isn’t doing anything out of the ordinary.
Unit tests are considered tests against code in memory, whereas integration tests are tests that require ANY external resources:
- Do your tests access any files? Integration test.
- Do you connect to a database to test something? Integration test.
- Are you testing business logic? Unit test.
As you’re beginning to surmise, when you build your application on another machine, cloud services do not have access to your database server and also may not have the additional files you need for each test to pass.
If you are accessing external resources, it may be a better approach to refactor your tests into something a little more memory-driven. I’ll explain why in Chapter 7, when we’ll cover unit testing.
Creating Environment Settings
Whether you are in the middle of a project or are clicking Create New Project… for the first time, you need a way to create environment settings for your web application.
In ASP.NET Core applications, we are given appsettings.json
and appsettings.Development.json
configuration files out of the box. The appsettings.json
file is meant to be a base configuration file and, depending on the environment, each appsettings
file is applied and only existing properties are overwritten to the appsettings.json
file.
One common example of this is connection strings and application paths. Depending on the environment, each file will have its own settings.
The environments need to be defined upfront as well. There will always be a development and release environment. There may be an option to create another environment called QA on another machine somewhere, so an appsettings.qa.json
file would be required with its own environment-specific settings.
Confirm that these settings have been saved for each relevant environment since they are important in a CI/CD pipeline. These environment settings should always be checked into version control with your solution/project to assist the pipeline in deploying the right settings to the right environment.
In this section, we covered ways to prepare your code for a CI/CD pipeline by making sure we can build immediately after cloning or pulling the repository down locally, why we should avoid relative-based file paths, and confirmed we were using environment-specific application settings, making it easy to build and deploy our application.
With your code checked in, we can now move forward and describe all of the stages of a common pipeline.