The goal of LUCID development is to create code that is easier to maintain and extend. Many times, maintainable is often confused for debuggable. Being able to easily debug code is important, but there is more to maintaining an application than fixing bugs. The environment in which an application is deployed is seldom fixed. As code moves through the software development process, it moves through different environments. In many cases, the full details of then environment may not even be known at the time development starts. Getting your code to change its behavior on the fly is only possible if you have carefully and thoughtfully interleaved configuration parameters into your code.
Most applications of reasonable complexity are really just parts of a larger system. Applications of this nature interact constantly with other applications such as databases, logging systems and third party services. Connecting to these external systems usually requires knowing an endpoint, a username and a password. The benefits of using configurations for these values include both security and personal sanity. If something goes wrong with one of these services you can quickly change the connection credentials. While that alone doesn’t secure a system, it does help to mitigate risks in the event of a system compromise.
In the past, I often felt that the more systems I had access to the more other respected and trusted me. I have learned, however, that trust and respect are not measured by system access. In fact, as I have matured, I have done my best to avoid gaining access to systems like production databases. When a system is compromised, the first group of suspects are those who were granted access. By storing these values in proper configuration files, I can immediately exclude myself from the group of people who may have slipped up. I have no idea what our production databases’ usernames are, let alone the passwords. I sleep easier at night knowing that I can’t possibly let that information slip out to an untrusted source.
In order to fully absolve myself of that responsibility, our code needs to be able to pull in configuration values from a source outside the code repository. If the production configuration lives in the same version control repo as the code I work with, I have access, and I cannot claim ignorance. Configuration files need to able to live external to the main code. The location of that file can live in a repository bootstrap file or can be known by convention, but the actual contents are something I never want to see. I will leave that responsibility to our Operations team.
Once you have separated out your configuration files, you can go about lacing that information into your code. Adding configurations typically isn’t the hard part; knowing what to configure is often more difficult. A good place to start is with code that involves external systems. Adding configurable values for the access credentials, timeouts and retry logic will help to isolate failures (more on that later). Being able to change these values without needing to redeploy code allows those supporting the application to change behaviors in response to a changing environment. If a third party service is having trouble delivering, turning down the timeouts prevents that system from negatively impacting the rest of the system.
Additionally, being able to configure the application to use different drivers or services makes the system easier to test and develop. There is no need to set up a redundant queuing system while unit testing. Injecting a mock system by adjusting runtime configurations helps to isolate the code under test and provide better testing results. The same is true for logging configurations. Different environments require different levels of logging and may use different logging backends. Easily manageable external configurations make adjusting the behavior of a system trivial.
Configurations make runtime behavior changes possible. These behavior modifications allow the code to adapt to an ever changing environment. When combined with runtime data from logs, configurations make it easier to identify and isolate sections of an application. The ability to isolate problem areas with configurations is a key component of LUCID development. The next post will explain in greater detail why it is important to use configurations to isolate sections of code.