This is the initial article about global values in configuration management. It will cover the core principles and give you a starting point. But configuration management is a topic with considerably more complexity than most people think. So please be warned that the article cannot cover all details that will be relevant for a real-world implementation. The latter will always require a bespoke approach.
When looking at global values in configuration the distinction between the conceptual side and the implementation is crucial. You can compare this to separating the analysis of a problem and the work towards its solution. In either situation it is a very bad idea to mix the two.
The conceptual side for the purpose of this article are the semantics. It is basically the question how truly global a value is. Surprisingly, there are not that many and we look into this in more detail below. The section about implementation aspects then addresses the crucial question how to actually do things.
Semantic considerations
A typical misconception is that a value that is used across multiple applications (i.e. Integration Server packages) qualifies as a global value simply because it is used in several places. Usually, that is wrong. Instead it is still just a value that is supposed to be used across applications. Examples would be the email address of a system owner or a technical connection parameter. (Use in several places usually means business-level dependencies that must be reflected properly by the implementation. A truly critical topic.)
A real global value is one that applies to the entire organization, regardless of division, busines unit, country, etc. A few examples:
- Company name
- Corporate web site URL
- Internal CA certificate
- Central SMTP server hostname
address.delivery=42 Main Street, Little Village
address.invoice=42 Main Street, Little Village
address.marketing=42 Main Street, Little Village
That the different objects are to be delivered to the same building does not change that. Also, think about future changes. If a new warehouse is opened in another location, the delivery address needs to be updated. So we now look at something like
address.delivery=1246 Side Street, Smaller Place
address.invoice=42 Main Street, Little Village
address.marketing=42 Main Street, Little Village
Sidenote: Default values are a pretty interesting subject. They are kind–of global, until another value comes along. But just like with the address example above, the problem is that it is usually the actual value and not its meaning that is standard until overridden (to avoid the terms global and default). Default values can exist on multiple levels: truly global, per organizational unit, etc. So they can be global, but this needs to examined on a case–by–case basis.
Implementation options
A proper configuration management solution provides multiple options for re–using a configuration value. Here are some guidelines that have shown to work well for non–trivial implementations.
In general, it is recommended to use global values as little as possible (see semantics above). Yes, they are more convenient initially. But unless extreme care is exercised, they pose a long–term maintenance risk, because it becomes difficult to trace where they are used. In addition, people may use them because at the time of the implemenation the value was correct, although the semantics did not fit (see addresses above). An example would be an SMTP vs. IMAP server hostname. As long as both functions reside on the same machine, this does not pose an issue. But if, e.g. for security reasons, a dedicated mail relay is introduced on a different machine, an SMTP send could end up on the IMAP server, where it would obviously fail.
Cross–package use
Instead of using global values, it is recommended to use what I call the “cross-package approach”. It means that I define a configuration value in one package and explicitly reference it from others.
As an example let’s assume the following scenario:
- Our company (ACME Corp.) runs a central CRM system. If someone needs to send an email to the owner, they are supposed to use a generic email address, in order to decouple things from the actual person.
- We want to define this email address in the Integration Server package that handles core aspects of the CRM system.
- There are other Integration Server packages that deal with specific aspects of the CRM. For our example let’s assume that we have a process (and hence separate package) for handling maintenance renewals.
- From this maintenance renewal process we sometimes have an exception and need to reach out to the system owner. Therefore we need the aforementioned technical email address.
[ Package: Acme_CRM ]
system.owner.email=crm.admin@acme.com
and then reference it from the renewals package
[ Package: Acme_CRM_Renewal ]
exception.notification.mail=${pkg:Acme_CRM;system.owner.email}
In my view this approach has a number of advantages.
- At design-time at makes it explicit from where a given value is coming.
- It is possible to perform an upfront check whether a referenced value is still available from the source package. This is very helpful for tests as part of a CI/CD pipeline.
- Slightly coming back to the conceptual side, this approach requires people to think from where they want to pull a value. This improves the understanding about the business domain and its dependencies, which is a huge bonus for the long-term evolution of the software.
There is probably more, but those reasons alone have been more than enough for me.
Truly global values
For values that are truly global from a semantics point of view, it is still recommended to stick with the cross-package approach. In this case there would simply be a package that does not hold any code/logic, but only global configuration values. Such a package could be named Acme_GlobalSettings
. Assuming we have the hostname of the corporate SMTP server in there as
[ Acme_GlobalSettings ]
smtp.hostname=mail.acme.com
another package would make use of that value with something like a cross–package interpolator. A line in the configuration file would then look like
notification.mail.smtp=${pkg:Acme_GlobalSettings;smtp.hostname}
There are rare exceptions when you cannot avoid, at least short-term, the use of global values. They usually fall into the category of “we have a bit of a mess right now and must live with that for a little while longer”. This mess can be on a technical as well as (more likely) an organizational level.
A typical organizational problem is how to introduce new global values. If the team responsible is overloaded and therefore cannot react within the required time-frame, what do you do? It will usually be more productive to start with a workaround and fix it during the next cycle, rather than saying “it is their fault”.
In closing
I could hopefully convey how fascinating the topic of global configuration values is. What I find particularly interesting is the connection to the topic of Domain-Driven Design. The latter is often only seen as relevant for code and application architecture. But in my view it is just as relevant for configuration.
If you want me to write about other aspects of this topic, please leave a comment or send an email to info@jahntech.com. The same applies if you want to talk how we at JahnTech can help you with your project.
One Response
This is extremely insightful. The clarity in explanation and the practical examples provided make complex concepts easily understandable, offering valuable guidance .