When initially encountering this factor, you might interpret it as advocating for monorepos, which advocate for housing the code for multiple projects within a single version control repository. However, this interpretation isn’t accurate.
The principle of one codebase dictates that a singular codebase should serve all deployment environments, such as development, staging, and production. This approach negates the practice of having separate repositories configured for each environment or, even worse, maintaining a repository with a production-specific fix that hasn’t been integrated back into earlier environments.
The primary goal here is to ensure consistency. By deploying code from the same repository across all environments, you significantly reduce the potential for inconsistencies and, consequently, unexpected bugs. This practice bolsters reliability and predictability across all stages of the application life cycle.
II. Dependencies: Explicitly Declare and Isolate Dependencies
Applications often rely on a multitude of external libraries and frameworks. A common mistake is to assume that a specific version of a library installed on one developer’s machine will be equally available on another’s, or even in a production environment. This assumption can lead to unexpected errors and the infamous “it works on my machine” defense.
To sidestep this issue, it’s critical to explicitly declare all dependencies, inclusive of their precise version numbers. This approach ensures all necessary components are readily available across all development and deployment environments, thus fostering consistency and eliminating potential errors.
Later in this chapter, you will see how containers can greatly facilitate this process, further enhancing the robustness and reliability of your applications.
III. Config: Store Config in the Environment
In the application of the one codebase principle, the challenge emerges of how to manage environment-specific configurations, like database connection details or API keys, which aren’t suited for inclusion in the codebase. The answer lies in utilizing the environment itself as the storage medium for these configurations, thereby allowing them to be injected directly into the application. This can be accomplished through environment variables or distinct configuration files.
However, when dealing with sensitive information such as API keys, passwords, or other credentials, storing them directly in the environment can introduce unnecessary security risks. Exposure of environment variables, especially in multitenant environments or open source projects, could lead to unintended access to these secrets.
To mitigate this, a more secure alternative involves the use of secret management services. These specialized services, such as Google Cloud Secret Manager, offer a secure and convenient method to handle sensitive data. They store, manage, and access secrets like API keys, passwords, or certificates in a centralized and secure manner. These secrets are encrypted at rest and in transit and can be accessed programmatically by applications that require them. They also provide auditing and versioning capabilities, which help to monitor and control access to your secrets.
So, while environment-dependent configurations are stored within the environment, sensitive data should be managed using dedicated secret management services. This combination ensures that each environment is correctly configured while maintaining the confidentiality and integrity of your credentials, hence enhancing the overall security posture of your application.
Tip
Storing sensitive information within the codebase poses a significant risk. Cybercriminals employ sophisticated, automated tools that scour code repositories like GitHub, looking for any vulnerabilities such as embedded credentials. Once discovered, these credentials can be exploited in numerous ways. For instance, cloud credentials could allow a hacker unauthorized access to private data. Even worse, they could harness these acquired privileges to spawn an army of cloud-based virtual machines for their purposes, such as Bitcoin mining. In such scenarios, you, the account owner, might end up bearing the brunt of substantial unforeseen expenses.