A Confluence page describing your dependency policy is documentation. A .repohelm.yaml file checked into your config repository is policy. The difference isn't format. It's enforceability, discoverability, and the gap between what the policy says and what actually happens in production.
Most teams write their first dependency policy after an incident. A CVE made it to production. A GPL library turned up in a commercial product. A package was pinned to a version with a known remote code execution vulnerability for months because no one had a process to surface it. The first policy draft reflects the trauma of that specific incident rather than a principled framework for the broader problem.
Here are four principles that separate policies teams follow from policies teams work around.
Principle 1: The policy should be wrong sometimes, and that should be okay
Teams write overly broad policies because they're trying to prevent every possible bad outcome in a single document. Every new vulnerability class gets a new rule. Every incident gets a new restriction. Over time, the policy accumulates rules that technically apply to a situation no one has encountered and might never encounter, while genuine risks slip through because the rule that would catch them is too expensive to enforce.
A better model is to write a policy that covers the 80% case well, build an explicit exception mechanism for the 20%, and track exceptions so they become data for your next policy revision. If an exception gets granted three times in six months, that's a signal that the rule is miscalibrated. If no exceptions are ever granted, that's either a sign the policy is working or a sign that teams are working around it without going through the exception process.
The exception path matters as much as the rules themselves. If requesting an exception requires a security review, legal sign-off, and a Jira ticket with a three-week SLA, developers will either route around the policy or do nothing and accumulate debt. If requesting an exception requires leaving a comment in the PR with a documented rationale, that's a bar most teams will clear and it creates a record you can audit.
Principle 2: Every rule needs an owner and a review date
Most dependency policies don't age well. CVSS severity thresholds that were calibrated in one threat environment may be wrong eighteen months later. License allowlists that reflected one legal team's position may need revision when the company's product distribution model changes. Blocked package lists accumulate entries from past incidents and rarely get pruned.
A policy rule without an owner is an orphan rule. Nobody defends it at review time because nobody can explain why it was written. An orphan rule that's causing friction will eventually get deleted without ceremony, sometimes by someone who doesn't realize it was there for a specific reason.
Adding an owner comment and a review date to each rule section takes five minutes and prevents years of policy drift. The format doesn't matter. What matters is that someone is accountable for each rule and that accountability has a time component.
Principle 3: The policy must be testable before you deploy it
One of the most common complaints we hear from teams adopting policy-as-code is the deploy-and-discover cycle. You write a rule, commit it, and discover in the first post-commit scan that it produces 300 violations across 40 repos, mostly false positives from a transitive dependency that appears in half the codebase. You roll back the rule, revise it, and try again.
This is painful enough that it leads teams to write overly permissive policies just to avoid the cycle. A policy that barely flags anything is a policy that doesn't require constant firefighting, even if it also doesn't provide meaningful security value.
The solution is to make the policy testable against a representative sample of your actual codebase before committing it. This is one of the features on our roadmap for Repohelm's policy sandbox mode: take a draft policy file, run it against your last 30 days of scan findings, and show you the diff between what the current policy would have flagged versus what the new policy would flag. Testing before deploying is standard practice for application code. Policy should be no different.
Until that kind of tooling is available for your specific setup, a reasonable proxy is to run the new policy in audit mode first: collect violations without enforcing, watch the volume for a week, then move to enforcement mode. Audit mode is less clean than a proper sandbox but it prevents the shock of discovering a new rule has made 200 PRs suddenly non-compliant overnight.
Principle 4: Make the violation message tell you what to do next
This principle sounds like a small UX concern. It is actually the single biggest driver of whether developers treat policy violations as actionable signals or as noise to be dismissed.
A violation message that reads "POLICY_VIOLATION: license_risk" tells the developer something is wrong but not what to do about it. A violation message that reads "Package acme-logger uses AGPL-3.0, which is blocked for production dependencies. To use this package, request an exception via the exception process in policy/README.md, or replace with MIT-licensed logger-lib (same API surface, available in your registry)" tells the developer exactly what their options are. The second message takes thirty seconds longer to write. The second message gets resolved. The first generates a Slack thread that ends with someone adding the package to the allowlist without understanding why it was flagged.
In Repohelm's PR descriptions, we include the policy rule that triggered the PR, the reason it applies to this specific package, and a short note on the remediation path. The PR description is the enforcement moment: if the developer reading it can't understand what the violation means and what to do next, the PR will sit in the queue or be closed without action.
The policy format question
YAML versus OPA/Rego is a real question for teams building this for the first time. The short answer: start with YAML. OPA is powerful, but it requires developers to learn a new language and a new evaluation model. YAML policy files can be read and understood by anyone who can read a configuration file. For most teams, YAML covers their requirements for the first year. Rego is for teams that have genuinely outgrown YAML expressiveness, not teams that want to future-proof.
We're not saying Rego is wrong. We're saying that the complexity cost of Rego is only worth paying when the policy requirements actually require it. A team that moves to Rego on day one because it's more powerful will spend more time maintaining the policy and less time on the governance work the policy is supposed to support.
The best dependency policy is the one that runs reliably, produces violations that developers understand, and gets reviewed and updated by a real person on a schedule. Format is secondary to those properties.