Skip to main content

default-over-else

Summary: Prefer default assignment over fallback else

Category: Style

Avoid

package policy

import rego.v1

permissions := ["read", "write"] if {
input.user == "admin"
} else := ["read"]

Prefer

package policy

import rego.v1

default permissions := ["read"]

permissions := ["read", "write"] if {
input.user == "admin"
}

Rationale

The else keyword has a single purpose in Rego — to allow a policy author to control the order of evaluation. Whether several else-clauses are chained or not, it's common to use a last "fallback" else to cover all cases not covered by the conditions in the preceding else-bodies. A kind of "catch all", or "default" condition. This is useful, but Rego arguably provides a more idiomatic construct for default assignment: the default keyword.

While the end result is the same, default assignment has the benefit of more clearly — and before the conditional assignments — communicating what the safe option is. This is particularly important for entrypoint rules, where the default value of a rule is a part of the rule's contract.

Exceptions

OPA v0.55.0 introduced support for the default keyword for custom functions. This means that else fallbacks in functions may now be rewritten to use default assignment too:

package policy

first_name(full_name) := split(full_name, " ")[0] if {
full_name != ""
} else := "Unknown"

Could now be written as:

package policy

default first_name(_) := "Unknown"

first_name(full_name) := split(full_name, " ")[0] if {
full_name != ""
}

Default value assignment for functions however come with a big caveat — the default case will only be triggered if all arguments passed to the function evaluate to a defined value. Thus, calling the first_name function from our above example is not guaranteed to return a value of "Unknown":

# undefined if `input.name` is undefined
fname := first_name(input.name)

Whether deemed acceptable or not, this differs enough from default assignment of rules to make this preference opt-in rather than opt-out. Use the prefer-default-functions configuration option to control whether default assignment should be preferred over else fallbacks also for custom functions. The default value (no pun intended!) of this config option is false.

Configuration Options

This linter rule provides the following configuration options:

rules:
style:
default-over-else:
# one of "error", "warning", "ignore"
level: error
# whether to prefer default assignment over
# `else` fallbacks for custom functions
prefer-default-functions: false

Community

If you think you've found a problem with this rule or its documentation, would like to suggest improvements, new rules, or just talk about Regal in general, please join us in the #regal channel in the Styra Community Slack!