Configurable Strings
June 4, 2026 · View on GitHub
Strings can be configured with a "base" starting root template and "arguments" to fill the template.
This encourages reusing common substrings and sharing them amongst features while allowing features to override parts of the string.
"base" and values in "arguments" can be strings, or objects that reference a file or a Liquid template.
Simple example:
{
"options": {
"greeting": {
"$type": "Optify.ConfigurableString",
"base": {
"liquid": "Hello, {{name}}!"
},
"arguments": {
"name": "World"
}
}
}
}
Result: { "greeting": "Hello, World!" }
Strings are built when the configuration is retrieved. Meaning that they will be built eagerly in Rust and cached when requested from a provider, speeding up your runtime for subsequent requests.
Overview
Configurable strings provide:
- Template-based string generation using Liquid syntax
- Variable substitution with configurable arguments in Liquid templates
- File-based templates for longer or reusable content in the base or arguments
- Inheritance and override capabilities across features, like any other configurable object
Enabling Configurable Strings
In .optify/config.json for the directory where you want to enable configurable strings:
{
"$schema": "https://raw.githubusercontent.com/juharris/optify/refs/heads/main/schemas/optify_config.json",
"areConfigurableValuesEnabled": true
}
Configurable values must also be enabled in the preferences given when getting options from an OptionsProvider.
Basic Structure
A configurable string is defined using the special type Optify.ConfigurableString with two main components:
- base: The template string or source
- arguments: Optional variables to substitute in the template
Syntax Options
1. Simple String
The simplest form is a plain string without any variables:
{
"greeting": {
"$type": "Optify.ConfigurableString",
"base": "Hello, World!"
}
}
Result: "Hello, World!"
2. Liquid Template with Variables
Use Liquid syntax for variable substitution:
{
"message": {
"$type": "Optify.ConfigurableString",
"base": {
"liquid": "Welcome to {{ name }}!"
},
"arguments": {
"name": "Optify"
}
}
}
Result: "Welcome to Optify!"
3. File-based Templates
Load templates from external files:
{
"greeting_from_file": {
"$type": "Optify.ConfigurableString",
"base": {
"file": "templates/greeting.txt"
}
}
}
Where templates/greeting.txt contains:
Hello from template file!
Result: "Hello from template file!"
4. File-based Liquid Templates
Combine file loading with Liquid templates:
{
"message_from_liquid_file": {
"$type": "Optify.ConfigurableString",
"base": {
"file": "templates/message.liquid"
},
"arguments": {
"app_name": "Optify",
"message_from_file": {
"file": "templates/message.txt"
}
}
}
}
Where templates/message.liquid contains:
Welcome to {{ app_name }}! {{ message_from_file }}
Where templates/message.txt contains:
This message is from a file.
Result: "Welcome to Optify! This message is from a file."
Overriding Arguments
One of the most powerful features of configurable strings is the ability to override arguments in other feature files while keeping the template intact.
Example: Base Configuration
feature_A.json:
{
"options": {
"welcome_message": {
"$type": "Optify.ConfigurableString",
"base": {
"liquid": "Hello, {{audience}}!"
},
"arguments": {
"audience": "World"
}
}
}
}
Example: Override Arguments
feature_B.yaml:
options:
welcome_message:
arguments:
audience: "Justin"
When features ["feature_A", "feature_B"] are applied, the result is:
{
"welcome_message": "Hello, Justin!"
}
Notice how only the arguments were overridden, not the template itself.
Nested Arguments
Arguments can reference other arguments using Liquid templates:
{
"complex_message": {
"$type": "Optify.ConfigurableString",
"base": {
"liquid": "{{ greeting }}, {{ name }}! {{ closing }}"
},
"arguments": {
"greeting": {
"liquid": "Welcome to {{ location }}"
},
"location": "Optify",
"name": "Developer",
"closing": "Enjoy your stay!"
}
}
}
Result: "Welcome to Optify, Developer! Enjoy your stay!"
Use Cases
1. Environment-Specific Messages
Different messages for different environments without changing code:
{
"api_endpoint": {
"$type": "Optify.ConfigurableString",
"base": {
"liquid": "https://{{ subdomain }}.example.com/api/{{ version }}"
},
"arguments": {
"subdomain": "dev",
"version": "v1"
}
}
}
2. Dynamic Error Messages
Provide context-aware error messages:
{
"error_message": {
"$type": "Optify.ConfigurableString",
"base": {
"liquid": "Failed to {{ action }} {{ resource }}: {{ reason }}"
},
"arguments": {
"action": "load",
"resource": "user profile",
"reason": "network timeout"
}
}
}
Best Practices
- Keep templates focused: Each configurable string should represent a single, cohesive message or value
- Use descriptive argument names: Make it clear what each argument represents
- Document complex templates: Add comments or documentation for templates with multiple variables
- Prefer file-based templates for long content: Keep configuration files clean by moving longer templates to separate files
- Test argument overrides: Ensure that overriding arguments produces the expected results
File Organization
When using file-based templates, organize them logically:
configurations/
├── feature_a.json
├── feature_b.yaml
└── templates/
├── emails/
│ ├── welcome.liquid
│ └── notification.liquid
├── ui/
│ ├── error.liquid
│ └── success.liquid
└── api/
└── responses.liquid
Liquid Template Features
Configurable strings support standard Liquid template features:
- Variables:
{{ variable_name }} - Filters:
{{ name | upcase }},{{ price | round: 2 }}
For full Liquid syntax documentation, see the Liquid template language documentation.
Examples and Tests
For more comprehensive examples and test cases, see the test suite for configurable values.
The test suite includes:
- Simple string configurations
- Template overrides
- File-based templates
- Complex nested arguments
- Various data type handling