Linter¶
Linting automatically checks your model definitions against your team's best practices and catches common mistakes before they cause problems.
When you create a Vulcan plan, each model's code gets checked against the linting rules you've configured. If any rules are violated, Vulcan tells you so you can fix the issues before deploying.
Vulcan includes built-in rules that catch common SQL mistakes and enforce good practices. You can also write custom rules that match your team's specific requirements. This maintains code quality and catches issues early, when they're easier to fix.
Rules¶
Linting rules are pattern detectors. Each rule looks for a specific pattern (or lack of a pattern) in your model code.
Some rules check that a pattern isn't present, like the NoSelectStar rule that prevents SELECT * in your outermost query. Other rules check that a pattern is present, like making sure every model has an owner field specified. Both types keep your code consistent and maintainable.
Rules are written in Python. Each rule is a Python class that inherits from Vulcan's Rule base class. You define the logic for detecting the pattern, and Vulcan handles the rest.
When you create a custom rule, implement four things:
-
Name: The class name becomes the rule's name (converted to lowercase with underscores).
-
Description: A docstring that explains what the rule checks and why it matters.
-
Pattern validation logic: The
check_model()method that checks your model code. You can access any attribute of theModelobject. -
Rule violation logic: If the pattern isn't valid, return a
RuleViolationobject with a message that tells the user what's wrong and how to fix it.
Built-in Rules¶
Vulcan includes built-in rules that catch common SQL mistakes and enforce good coding practices. These rules catch real issues seen in production.
For example, the NoSelectStar rule prevents using SELECT * in your outermost query. SELECT * makes it unclear what columns your model produces, which can break downstream models and make debugging harder.
Here's what the NoSelectStar rule looks like, with annotations showing how it's structured:
Here are all of Vulcan's built-in linting rules:
| Name | Check type | Explanation |
|---|---|---|
ambiguousorinvalidcolumn |
Correctness | Vulcan found duplicate columns or was unable to determine whether a column is duplicated or not |
invalidselectstarexpansion |
Correctness | The query's top-level selection may be SELECT *, but only if Vulcan can expand the SELECT * into individual columns |
noselectstar |
Stylistic | The query's top-level selection may not be SELECT *, even if Vulcan can expand the SELECT * into individual columns |
nomissingaudits |
Governance | Vulcan did not find any audits in the model's configuration to test data quality. |
User-Defined Rules¶
Built-in rules are good, but every team has different standards. Write custom rules that enforce your team's specific best practices.
For example, make sure every model has an owner field so you know who's responsible for it:
Put your custom rules in the linter/ directory of your project. Vulcan will automatically find and load any classes that inherit from Rule in that directory.
Once you've added a rule to your configuration file, Vulcan will run it automatically when:
- You create a plan with vulcan plan
- You run the
vulcan lintcommand
If a model violates a rule, Vulcan will stop and tell you exactly which model(s) have problems. Here's what that looks like, in this example, full_model.sql is missing an owner, so the plan stops:
$ vulcan plan
Linter errors for .../models/full_model.sql:
- nomissingowner: Model owner should always be specified.
Error: Linter detected errors in the code. Please fix them before proceeding.
You can also run linting on its own for faster iteration during development:
$ vulcan lint
Linter errors for .../models/full_model.sql:
- nomissingowner: Model owner should always be specified.
Error: Linter detected errors in the code. Please fix them before proceeding.
Use vulcan lint --help for more information.
Applying Linting Rules¶
Specify which linting rules a project should apply in the project's configuration file.
List which rules to run under the linter key. Globally enable or disable linting with the enabled key (defaults to false, so you need to turn it on).
Important
Don't forget to set enabled: true! If you don't, Vulcan won't run any linting rules, even if you've specified them.
Specific Linting Rules¶
Use just a few specific rules. List them in the rules array. Example that enables two built-in rules:
All Linting Rules¶
Enable all rules. Use "ALL" instead of listing them individually. This runs every built-in rule plus any custom rules you've defined:
Sometimes you want almost everything, but one or two rules don't fit your workflow. Use "ALL" and exclude specific rules with ignored_rules:
Exclude a Model from Linting¶
Sometimes a model legitimately needs to violate a rule. Maybe it's a legacy model you're migrating, or there's a special case. Exclude specific models from specific rules (or all rules) by adding ignored_rules to the model's MODEL block.
Here's an example where we exclude one model from one rule:
Rule Violation Behavior¶
By default, when a rule is violated, Vulcan treats it as an error and stops execution. This ensures you fix issues before they make it to production.
Sometimes you want a rule to be a suggestion rather than a hard requirement. Maybe it's a style preference that's nice to have but not critical. Put the rule in warn_rules instead of rules. Violations are still reported, but they won't stop execution:
Vulcan will raise an error if the same rule is included in more than one of the rules, warn_rules, and ignored_rules keys since they should be mutually exclusive.