Andreas Finger

Settings - A clean way to handle custom configuration values

Ruby Unconf 2019

00:03:47.000 I suspected I said missing man service.
00:03:49.459 Method missing references.
00:03:54.420 There's no quickness; listen to people.
00:05:09.639 It is not dim.
00:05:14.340 Oh, thanks.
00:05:51.080 So much.
00:06:32.919 I wasn't clear on which microphone to speak into or if we're not speaking into any microphone. Can you all hear me okay? Yeah.
00:06:43.360 Hello, my name is Andreas, as you already know. I have lived a long time here in Hamburg. I started my Ruby developer career here, but nowadays I live in Barcelona and usually work remotely.
00:06:46.090 I would like to introduce 'Settings' to you, or better yet, to speak about a clean way of adding custom configuration values to your applications—any Ruby application. Not only Rails, which has its own mechanism already, but if you're using a heavy Snott or anything else, you won't find the best way of doing it already there.
00:06:59.379 So, you have to come up with something yourself. It will not use method missing, and it just fixes that typo. Thanks.
00:07:32.289 So, the motivation for this talk is, while we were working at Solaris Bank, we didn't have any Rails apps, but many different Ruby apps.
00:07:39.039 All of them had some way of handling custom configuration. At some point, we had an issue in one of our apps; it didn't behave as expected.
00:07:46.660 Several experienced developers spent a lot of time figuring out why it broke and what was actually broken. It was because we had such a convoluted custom configuration chaos that we didn't know how and where environment variables were actually set.
00:08:07.389 In development and testing, we had one way of setting up, but in the other environments, we would assume that the method is not used.
00:08:20.919 Maybe it was the environment where it's set up, the Docker setup, or maybe it's actually coming from the server or whatever custom infrastructure provides those environment variables.
00:08:46.510 We also found out that we were reading the values more than once, handling them in different ways. The load order was chaotic.
00:09:02.079 It took us time to figure out what was broken, and then we decided we had to clean this mess up. We created a much nicer system to handle it, a system that I'm still using not only for Ruby apps but even in my Rails apps nowadays. It's small, it's compact.
00:09:40.810 One of the learnings was not to read from the environment directly in your applications. You want to do it once at the boot time of your application, at the very beginning. These environment variables should be stable throughout the lifecycle of your application, from boot to shutdown, and you should then rely on those values in your code.
00:09:52.770 You also don't want to silently ignore typos. Maybe while developing, you had a typo in your environment variable name, and you copied it into the code—but in the other environments, it's spelled differently. The application will inform you about that, and you will realize there’s a bug somewhere.
00:10:58.909 It is important to have a helpful error message. You will also want to use default values during development and testing. That's what you use it for; to set default values.
00:11:05.040 We don’t want an external dependency like setting up environment variables or reading from them to set the values in your application. It's about having a lightweight approach to keep your settings organized and accessible.
00:11:46.990 So we came up with a mechanism to load this single file before anything else, ensuring that everything we need is accessible from the start. This way, we avoid a mixture of configurations from different setups and maintain a clear load order.
00:12:51.850 I mentioned earlier that it’s a small amount of code. Let me show you the essential part, starting with a class called Settings. It includes a class method called Register. We pass a variable name as a parameter.
00:13:01.330 Inside this method body, we employ a bit of meta-programming. If you're new to Ruby, this might look confusing at first, but let me break it down. The first line defines a single method, which we usually call a class method, and it captures the variable name from the parameter.
00:14:18.760 Next, we use an instance variable to hold the value from the environment. This value is fetched from a hash in Ruby using the variable name transformed to uppercase, because environment variables are by convention written in all uppercase letters. The implementation ensures that if the environment variable does not exist, it raises an error.
00:15:34.150 However, we also needed a way to set default values. So, I adjusted the code to add a second parameter for default, which has a default value of nil.
00:15:54.290 Now, when the environment variable does not exist, it can return a specified default value instead.
00:16:22.80 I'll demonstrate how to use this system. When we call register with the environment variable name, it will either fetch the corresponding value or fall back to the default if the environment variable is absent.
00:17:36.640 This flexibility empowers me to easily adjust configurations without modifying the core application code, allowing for overrides based on local file settings. Plus, we can keep sensitive configurations out of the main codebase.
00:18:21.160 I've gathered all of this functionality into a simple mechanism to register and manage those values more effectively. It alleviates the risk of config blocks littering the code, ensuring cleaner, more maintainable code.
00:19:24.920 The system we've created is concise and allows us to have a consistent approach to custom configuration, which is remarkably easier as everyone can benefit from it. Residual complexity has been removed, and now any developer can engage with the system effortlessly.
00:20:07.640 As I mentioned previously, all custom configuration values will be declared in one file. This makes it simple to grab code for usage in different environments.
00:21:15.640 By using this method, you can eliminate the ambiguity of environment variable names that often leads to confusion and errors, ensuring that all settings come from a single source for clarity.
00:22:00.480 The method I outlined has shown strong results across various projects. Should you ever come to Barcelona, I invite you to visit our office where we hold user group meetings every third Thursday of the month.
00:23:18.840 I would be happy to help anyone looking to implement this system or similar solutions. Thank you all for coming to this talk and for your time.
00:24:38.430 I’m open for questions. Instead of handing out a microphone, feel free to come up and have a discussion right here. Thank you very much for your attention. I know food is available after, so let's enjoy that too!