Configurable behavior must be isolated per plugin/consumer instance, and derived/internal values must not be injected into the schema-defined conf object. This prevents cross-request/config leakage and keeps schema validation predictable—especially during deprecations.
Apply:
conf as schema input: if you need computed/compiled data, create a separate table and pass it alongside conf.replaced_with to “magically” reconcile dereferencing.Example (instance-scoped state instead of module locals):
-- BAD: module-level mutable config shared across instances
-- local use_proto_names = false
-- function protojson:configure(options) use_proto_names = options.use_proto_names end
-- GOOD: store config on the instance
local protojson = {}
protojson.__index = protojson
function protojson.new(options)
return setmetatable({
use_proto_names = options.use_proto_names or false,
enum_as_name = options.enum_as_name or false,
emit_defaults = options.emit_defaults or false,
json_names = options.json_names or {},
}, protojson)
end
function protojson:decode_to_json(msg_type, msg)
-- use self.use_proto_names etc.
end
Example (don’t mutate conf with non-schema fields):
function OpenTelemetryHandler:log(conf)
local options = {}
if conf.resource_attributes then
options.compiled_resource_attributes = otel_utils.read_resource_attributes(conf.resource_attributes)
end
otel_traces.log(conf, options) -- pass alongside conf
end
Enter the URL of a public GitHub repository