Monday, August 22, 2022

Configuration VS. Code

 



In this post we will review a bad design pattern repeating itself in various products development projects. We will provide an example of such, and discuss how can we avoid it.


TL; DR

If the project configuration starts looking similar to code, use code not configuration.


Why is Configuration Required?

Many projects have configuration files. The configuration in the file can dynamically change the code behaviour. 

Why do we keep configuration? An alternative would be to use consts, and ask our code user to modify the consts, and to write another code that will use our code. But this is something we cannot ask from an end user which is not a software engineer, so we use configuration.


Right?

Always right?


Sample Project


Let have an example of a real project that I was part of. The project is a general installation management for various components in the company. Each component requires some registry settings that are read from the user, and some installation scripts. Each component might be depdend of other compoents, In addition, we need to manage a list of what is installed, and where.

Hey! I have an idea! Let make it an XML configuration file:


<component>

  <name>OS Agent</name>

<dependency>Memory Agent</dependency>

  <input>

<text>serverName</text>

<text>installationFolder</text>

</input>

<installationScript>install_os_agent.sh</installationScript>

</component>


Look great, right?

Well, yes. Now anyone can use our installer by simply creating a configuration file. No code is required. That's great. Lets send it to our company users!


Oh... There is a requirement from the Redis Agent that in case the redis type is cluster, we should run another script. 

Mmm... No problem, we can add simple logic in the XML. Here we go:



<component>

  <name>Redis Agent</name>

<dependency>OS Agent</dependency>

  <input>

<text>serverName</text>

<text>installationFolder</text>

<text>redisType</text>

</input>

<installationScript>install_redis_agent.sh</installationScript>

<if type="equal">

<input>redisType</input>

<const>cluster<const>

<do>

<installationScript>install_redis_cluster.sh</installationScript>

</do>

</if>

</component>



Great! We did it.

Oh.. The kubernetes agent requires us to loop on all the servers list, and run a script for each on.

Sure, we can do that:




<component>

  <name>Kubernetes Agent</name>

  <input>

<text>servers</text>

</input>

<installationScript>install_kubernetes_agent.sh</installationScript>

<loop>

<input>servers</input>

<do>

<installationScript>add_server.sh</installationScript>

</do>

</if>

</component>



Wait, some of the components are using the exactly the same list of installation scripts, let's not repeat the same configuration. Let use includes:



<component>

  <name>Grafana Agent</name>

  <input>

<text>servers</text>

</input>

<installationScript>install_grafana_agent.sh</installationScript>

<include>common/common_scripts.xml</include>

</component>


One last thing: some of the teams said that the configuration is great, but it is a bit complicated. Would it be possible to create a GUI to manage the configuration? A wizard to create actions in the configuration, and to edit existing configurations will be great...


End of Story


So, you end up rewriting a new programming language in XML. Anyone who want to use this requires eduaction resources (time and teachers). Debugging issues in the configuration is possible only by the team who owns this code. Any small improvement which is built-in within programming languages requires development time. This is a living hell. How did we got here?


How to Avoid?


To avoid this. Once we identify logic in our configuration that control the actual configuration we should stop, and rethink the design. In most cases the good solution would be to supply a code library that other teams could use as libraries in their own code. No more code in a configuration file, but codein a programming language that anyone knows, and can debug.

While it seems quite obvious when reading this post, this mistake repeats itself over and over again. It is very tempting to avoid code and have a full configurable component that does anything you need, but this is a wrong point a view. We should treat projects as tools, expert at their domain, but nothing else. Other functionalities should be owned by the tool users, hence enabling them with any possible customization they need.



No comments:

Post a Comment