Every app developer should move their config to the cloud. Here’s why.
How I Overcame the Hard-Coding Life, and You Can Too.
I’m going to tell you a secret. I’ve been writing iOS Apps since before (!) the App Store opened in 2008. I’ve made a lot of mistakes. Many of them I’ll never admit to. But for one mistake, it’s time to come clean:
I’m a hard-coder.
I don’t mean I’m a hard coder like I’ve lived in some alternate developers-as-gangsters reality in which the longer you write code the harder, faster, better you get. I just mean that I hard-code shit in my apps.
Paddings, colors, line-widths, API Keys. You name it, they’ve probably been hard-coded into my app. They’ve made their way in a binary all the way into the App Store and lived there like a permanent fossil. But there’s a better way. I want to show it to you. Don’t be like me. Don’t live a hard-coding life.
I’m going to talk at you about two things: constants, and cloud config.
The first is straightforward: Many of your app and visual settings should be declared as constants. That way you can change them easily and not dig through your code.
Second, let’s talk about cloud config. This is when things get interesting. You should take some of those constants and make them variables on a server. By doing this, you can change how your app behaves even after it’s in the App Store. Sometimes it’ll help you release new features. Better yet, they can save your ass if you find a bug.
Take it from me, this is magical. It’s like adding radio-controller to your cheap-ass Hyundai (or magic time-machine DeLorean, whatever).
Constants and You: A Love Story
Let’s go back to constants and make sure we’ve covered our bases. You probably already do this, so feel free to skip to the next section.
In case you don’t use constants for your settings and details, here’s a quick run-down of things I tend to make into settings constants in my apps:
I’ve learned to live my life as an iOS developer like a gazelle in the Serengeti. At any point, through the dense brush, a designer or PM can lunge at you with UI tweaks or “experiments”. (I live in constant fear.)
Here’s some code:
// Top of your implementation file
static CGFloat const CELL_PADDING_Y = 8.0;
static CGFloat const CELL_PADDING_X = 8.0;
static CGFloat const IMAGE_SIZE = 50.0;// Using it later
CGRect imageRect = CGRectMake(CELL_PADDING_X,
imageView.frame = imageRect;
One of my favorite apps I’ve worked on is Cluster. In fact, many of the learnings being dropped on you right now are from us having built — and incessantly tweaked — that app. When Brenden wanted to help in designing the app, I made a lot of these types of constants at the top of the files. This way, he could tweak the UI and commit the changes without me having to get involved.
Emails, URLs, and Social Media handles
Don’t hard-code these into your functions. They’re going to change eventually. I’ve definitely forgotten to change our support email address in our code before, and then in the next release no one could contact us. Make them constants like the UI details above.
Trying out new app behaviors
This one will make your code-housekeeping senses tingle with the tumult of a thousand tiny bells, but bear with me:
As you build your app, you’ll be iterating and adding new features and changing behaviors with every major release.
Until you’re sure the new behavior is what you want, you should make a “feature constant”, and gate your new stuff behind that. This lets you keep your old behavior alongside the new behavior. When you’re building apps, it’s critical to test things side-by-side. Doing git reverts or switching git branches just to try different things takes too long. So keep both versions, and use a “feature constant” to switch quickly between the two. It’ll give you the best kind of immediate comparison. (Note: When you’ve decided on one over the other, then please go and delete the unused code. Future You™ will thank you for it.)
There are a bunch of A/B testing tools that help you do this, like Optimizely, Apptimize, Mixpanel. Even Amazon’s in the A/B testing game! If you’re already using one, then high-five/fist-bump to you. If you’re not, then try one out. Some of them are simpler than others to set up, so look around for one that fits.
Cloud Configuration is a Dream
If you’ve already done the things I described above, then congratulations! Now let’s take some of those constants you have in your code, and turn them into variables in the cloud!
Doing this makes your app really powerful. Now you can control things like your UI details and feature settings, all while the app is in people’s hands.
Remember how you could control your DeLorean? Now you can do some serious shit to your app.
Here are some cool things you can do with remotely controlled config:
Adjust Your UI (revisited)
Same as above, but now with remote control! Maybe your image hosting service got an upgrade, so you can change to higher resolution images and make them larger. Awesome, you can remotely change the size of your image views!
Another example: If you’re not sure what the hell your “Sign Up” button should say (Tip — try: “Sign Up”, “Get Started”, “Hand Over Your Firstborn”), you can now make a small configuration variable on your server, and then change it after you ship.
Note: You could use an A/B testing tool to accomplish all sorts of major changes if you want, but I’m trying to get you to change some simple variables remotely, not run complicated “campaigns”. If you already use an A/B testing tool, then keep using it, but run some smaller changes too!
Think about it: Sometimes it’s the very little things about your app that get people to like it or hate it. Having bits of UI that you’re unsure about, but can remotely configure means you can quick make small changes with a large group of people. It’s really useful in a live user test.
Emails, URLs, and Social Media handles (revisited)
Apps change Twitter handles and even homepage URLs all the time. There’s no reason this should be hard-coded into your app. Maybe you’ll finally convince the squatter to sell you that domain you’ve wanted forever. Maybe you’ll change help desk providers (Hello Zendesk) and need to change your support email address. Maybe the Libyans will be back again to take your precious bit.ly away.
When we launched Cluster, we totally loved getcluster.com — but obviously we jumped at the opportunity to purchase cluster.co. If we had been able to update our app on the fly back then, the transition would’ve been seamless.
3rd-Party API Tokens
Ok. I’ve admitted to hard-coding stuff in the past, but now it’s time for you to come clean, too. You hard-code your 3rd party API tokens. Your S3 Access Key is sitting there as a constant in your app delegate. No, no, it’s ok. We’ve all been there.
Let me tell you about a time when I was burned by this: At my last startup, I was integrating a public-image-search service, for a “minor” feature, and so I incorporated my API key into the app’s binary. I figured if I ever ran close to the usage quota, we could pay to increase the limits.
When I accidentally hit the quota, it turned out that I had to provision a new key with a new quota, and I couldn’t just change the quota on my existing key. The “minor” feature generated tons of support emails and was completely broken for days while we waited for an expedited App Store review.
It’s not worth it. Join the closest post-traumatic-hard-coding community near you today!
Increasing or Decreasing Limits
When we launched video support into Cluster, we weren’t sure what kind of load we would experience on our video uploader. So we set a maximum duration for the video: 15 seconds. Since we knew that this limit would be going up, we built a custom config API call to give us the new time limit for video uploads.
After scaling up our backend from growing traffic, we were able to increase the limit to 60 seconds without re-submitting a new build to the App Store. Magic!
There’s no reason to hard code this stuff into your app. You might discover later that iPad Pros require better-quality uploads! Don’t make your users wait for a new version to start uploading better content.
Which In-App Purchases to show
If you have any in-app purchases in your app, you will probably want to offer holiday specials, and you’ll probably want short-term sales from time to time. Hard-coding this stuff in your binary is a fool’s errand.
A long time ago, before remote configuration, we were doing an in-app purchase promotion where the price would change at a certain date. Effectively, I was hard-coding a date into my app and showing a different in-app purchase. This, clearly, was a bad idea. But my server team didn’t have the bandwidth right then to make this a remote config, so I crossed my fingers and hoped that the in-app purchase would be different on the right date. You definitely should control this from your server(s).
Use Different Values Based on Device, Software Version, OS, etc.
In whatever remote config system you use, you should send up some local device and app settings, like:
- Hardware Model (e.g. iPhone7,1)
- OS (e.g. iOS 9.2)
- App Bundle, Version and Build(e.g. com.myapp.TheBestApp, 3.4.1, 34)
- Current Locale (e.g. en_US)
That way, you can set additional conditions on your config keys based on those settings.
For example, say a new iPad comes out (hello, iPad Pro!), and you realize it can handle way bigger images in your image-editing app, you can have it return a different value for a “maxImageSize” key.
Default Values are Still Useful
I know I said you should leave your hard-coding lifestyle behind, but I’m here to say that local values still have their place. One of the obvious questions about moving config the cloud is, of course, what happens during catastrophic network conditions.
If you have some remote config on your server, but the network is down on the very first time your app tries to reach it you’ll need some kind of fallback. You should use your previous, local hard-coded value (or some reasonable, useful value).
- First, your remote config fetching code should use whatever value it got on the last fetch.
- If that’s not available, look at the config values it cached from the server (and your remote config fetcher should definitely be caching these values).
- If that’s not available, there’s where your locally supplied, hard-coded value comes in to save the day.
Here’s a simple example. Let’s assume my remote config system periodically fetches all my config keys and keeps a local copy. Then I can have a simple call like this without async handlers:
let maxVideoDuration = RemoteConfigForKey("maxVideoDuration", 60.0)
In this case, I am asking our remote config system to give me the remote value for “maxVideoDuration”. The second parameter, 60.0, is a reasonable, default value (hard-coders rejoice!). If my system had never been able to retrieve the config keys, then it wouldn’t have a local copy, and therefore should use this default value.
I think it’s a simple solution to an unlikely but thorny problem.
Remote Configuration is Worth It
You might be thinking, remote config sounds cool, but it seems like a pain in the ass just for some small variables. We used to feel that way too. Trust me, having the ability to make things remotely configurable will change how you build your apps.
We put off building this for ourselves. Once we made one config variable (our uploader video duration), though, we started to add config all over our app.
I still have local constants in my app. That is just a good habit to have. However, some of the things that I used to hard code are now also configured remotely. That way, I have choices that I can make at run-time instead of compile-time. And for me, it’s a been a powerful yet simple change to my workflow.
Ideally, I’d recommend talking to your API team (or developer). Maybe build something on a server yourself (here’s a guide). If that won’t work, there are some tools people have already built that can help. Mattt Thompson’s GroundControl looks awesome.
Just put some of your app details on the cloud. Make it easy to add new config variables. Just. Go. Remote.
Please let me know what you think on Twitter!