Letting Coding Agents Lint Their Own PL/SQL with dbLinter

Coding agents write plenty of PL/SQL, but they need a feedback loop to write it well. Here's how to hand dbLinter to Coding Agents and teach it to check its own work.

Agents write code, but is it good code?

Coding agents are here to stay. They have become genuinely useful for writing PL/SQL, and they are only going to get better. But those productivity gains come with a risk: it’s easy to lose control over the sheer volume of generated code.

We usually trust a senior developer’s code more than a junior’s — experience shows up as consistently following standards, conventions, and best practices.

Wouldn’t it be nice if you could be sure that coding agents write code that follows your standards, conventions, and best practices?

What is dbLinter?

dbLinter is a static code analysis tool for SQL and PL/SQL. It ships with over 180 rules covering naming conventions, security, error handling, and general coding standards. The rules are configurable so you can match your own organization’s standards.

Most people use the VS Code extension for in-editor underlines, docs, and quick fixes. For agents the useful surface is the CLI.

Running dbLinter from the CLI

You can grab the CLI from the dbLinter releases page on GitHub. Download the build for your platform and make sure it is on your PATH so the dblinter command is available everywhere.

dbLinter needs a config file that holds execution settings and an access token so it can fetch the configuration. As I have a main configuration that I use in most projects I want to store it in a single place and point at it from any project. I keep it in my home directory and point at it with an environment variable set in my shell profile (e.g. ~/.zshrc):

export DBLINTER_ENV="$HOME/dbLinter/dblinter.properties"

dblinter.properties is a simple key=value file:

tenantName=UNITEDCODES
userName=PHILIPP@EXAMPLE.COM
accessToken=******
configName=Default
parallel=2
outputFormats=sarif
outputName=./.dblinter/check

With that in place, running a full check from the project root looks like this:

dblinter --options="$DBLINTER_ENV" check

Or, when I only want to lint a specific part of the codebase:

dblinter --options="$DBLINTER_ENV" check src/packages

The results are written to a SARIF file in the project root at ./.dblinter/check.sarif.sarif. That is the file the agent reads to find out what needs fixing.

A cleaner setup for teams: commit the config, keep secrets in the environment

The setup above keeps everything in one personal file outside the repo. That works, but it means every team member has to recreate the same tenant, config name, and output settings by hand — and nothing stops those from drifting apart over time.

There is a nicer split. By default the CLI looks for a dblinter.properties in the current workspace, so you can commit that file to the repo — just without the sensitive bits. Any property you leave out of the file can be supplied through an environment variable instead, named DBLINTER_<OPTION> with the key in screaming snake case. So accessToken becomes DBLINTER_ACCESS_TOKEN, userName becomes DBLINTER_USER_NAME, and so on.

That lets you commit a dblinter.properties with only the shared, non-secret settings:

tenantName=UNITEDCODES
configName=Default
parallel=2
outputFormats=sarif
outputName=./.dblinter/check

And each developer keeps their personal credentials in their shell profile:

export DBLINTER_USER_NAME="philipp@example.com"
export DBLINTER_ACCESS_TOKEN="******"

Now the config that should be identical for everyone is version-controlled and reviewed like any other code, while the secrets stay personal and never touch the repo. Best of all, because the CLI finds the workspace file automatically, the command your agent runs collapses to just:

dblinter check

No --options flag, no global path to point at. That is the version I put in a shared CLAUDE.md.

(Thanks for Philipp Salvisberg for the tip about the dblinter.properties file!)

Teaching your agent with CLAUDE.md / AGENTS.md

Here is the section I add to my CLAUDE.md (works the same in AGENTS.md for other providers). It gives the agent the commands, tells it when to run them, and sets the expectation that a clean report is part of “done”:

## Linting PL/SQL with dbLinter

After writing or changing any SQL or PL/SQL, lint it with dbLinter and fix
every violation before considering the task complete.

Run from the project root. The committed `dblinter.properties` holds the shared
config; personal credentials come from the `DBLINTER_USER_NAME` and
`DBLINTER_ACCESS_TOKEN` environment variables set in the shell profile. Never
put credentials into the committed properties file.

Check everything:

```sh
dblinter check
```

Check only specific paths (faster, prefer this when you know what changed):

```sh
dblinter check src/packages
```

Then read the report at `./.dblinter/check.sarif.sarif`, address each finding,
and re-run until it is clean.

For a genuine false positive or a deliberate exception, suppress the specific
rule inline and always give a reason. Scope is controlled by where you place the comment (line / block / file):

```sql
-- @dblinter ignore(<rule>): <reason>

-- @dblinter ignore(G-9108): p0-p4 intentionally mirror the apex_lang.message parameter names they are passed to
```

Do not disable rules broadly, and do not ignore a finding just to make the
report pass — only suppress with a real, written justification.

A few things I found worth spelling out explicitly, because agents take instructions literally:

  • Make it non-optional. Define done. “Lint and fix before the task is complete” turns the linter into part of the definition of done, not a nice-to-have the agent skips when it is in a hurry.
  • Prefer path-scoped checks. Pointing the agent at the paths it just touched keeps runs fast and the report focused on relevant findings.
  • Constrain the ignore escape hatch. Left unsupervised, an agent will absolutely suppress a rule to make a report green. Requiring a written reason keeps that honest and reviewable.

Because these instructions, and the dblinter.properties config alongside them, live in the repo, they get committed and shared with the whole team. As long as everyone has the CLI installed and their DBLINTER_USER_NAME / DBLINTER_ACCESS_TOKEN set, every developer’s agent lints against the exact same standards. The credentials stay personal and out of the repo; the standards are version-controlled and consistent for everyone.

Doing the same on Windows

The workflow is identical on Windows; only the way you set the credential environment variables changes. In PowerShell, set persistent user-level variables once with setx (they take effect in new terminal sessions):

setx DBLINTER_USER_NAME "philipp@example.com"
setx DBLINTER_ACCESS_TOKEN "******"

After that the command is the same everywhere, because the CLI picks up the committed dblinter.properties and the environment variables automatically:

dblinter check
dblinter check src/packages

Everything else — the SARIF report at ./.dblinter/check.sarif.sarif, the inline suppressions, and the agent instructions above — stays exactly the same.

Wrapping up

Linters were built to give humans fast, precise feedback about code quality. It turns out agents benefit from exactly the same thing, arguably even more, since they have no intuition to fall back on. By handing dbLinter’s CLI to Claude Code and writing down how and when to use it, I get PL/SQL back that isn’t just code that compiles, it’s code that follows the standards I actually care about. That’s how to stay in control in the age of AI-generated code.

If you already use dbLinter in your editor or your pipeline, wiring it into your coding agent is a small addition with a real payoff. And if you don’t yet, it is a good reason to take a look. The majority of features are free to use for solo devs.

Other Posts

Comments

Loading comments...
Homepage All Blogposts

AI disclaimer: I spend hours writing my blog posts by hand, adding my own thoughts and experiences to them. In my view, purely AI-generated content lacks that human depth and isn't worth publishing. I only use AI for research and editing assistance.