The problem: which bug do we fix first?
Most backlogs put a typo in a log message and a double-charge bug in the same list. Both are "bugs", and both can have a small title. Without a shared model, the order depends on who shouts loudest.
CEBIL is a small scoring model for operational risk. You score each component once, and then you score each ticket in a few seconds, using the score of its component. It helps answer three questions:
- Is this component important to everyone, or only to a niche?
- If it fails, will anyone notice, and how bad is it?
- Among the open tickets, which one removes the most risk for the least work?
CEBIL stands for Criticality, Exposure, Business visibility, Impact and Legal. Three more attributes complete the model: Stability, Nature and Comple*X*ity. I explain every letter below.
This model is for triage of operational work: bugs, incidents and reliability tasks. It is not a tool to rank new features.
Part 1: The alternatives, and where they fall short
Several good frameworks already exist. None of them was designed for operational risk, so each one leaves a gap.
| Framework | What it scores | Good for | Gap for operational risk |
|---|---|---|---|
| RICE | Reach × Impact × Confidence ÷ Effort | Ranking new features | "Impact" describes gain, not damage. There is no idea of failure or likelihood. |
| WSJF | (Business value + Time criticality + Risk reduction or opportunity) ÷ Job size | Sequencing work across a portfolio | Risk is one input among three, so reliability work competes with features in a single ratio. |
| Cost of delay | Economic loss per unit of time | Deciding what to expedite | It needs money estimates, which are hard to guess for small bugs. |
| FMEA | Severity × Occurrence × Detection | Analysing how a component can fail | It works per failure mode, so it is too heavy to run on every ticket. |
CEBIL borrows from all four: risk as consequence × likelihood (FMEA), value over effort (RICE and WSJF), and the idea that some work skips the queue (expedite, in cost of delay). Two things are different. The component scores are static, so a ticket only needs two new inputs. And a few rules override the ratio on purpose.
Part 2: The framework
CEBIL works in two stages. The component stage is static and reviewed every quarter. The ticket stage is fast and happens for every bug or change.
a) Component: the letters
Five letters measure the consequence of a failure. Each one is a score from 0 to 3, except Legal, which goes from 0 to 2.
| Letter | Question | 0 | 1 | 2 | 3 |
|---|---|---|---|---|---|
| I Impact | What happens in a typical failure? | No visible impact | Minor inconvenience | Part of a flow degrades | Core flow blocked, or data corrupted |
| C Criticality and recovery | Is it a hard dependency, and how hard is recovery? Use the worse answer. | Optional, and it recovers by itself | Helper, or easy manual recovery | Replaceable, or recovery needs coordination | Hard dependency, or recovery needs data fixes |
| E Exposure | Who is affected? | Internal only | Small group | Many users or teams | Everyone, or external parties |
| B Business visibility | How fast does the business notice? | Invisible | Noticed eventually | Support tickets | Immediate calls, legal or financial impact |
| L Legal | Is there regulatory, contractual or audit risk? | None | Possibly risky | Absolutely risky | - |
Three more attributes are not part of the consequence sum. Each has a different job.
| Letter | Job | Low | Middle | High |
|---|---|---|---|---|
| S Stability | Likelihood. It multiplies the consequence. | 1: stable, rare incidents | 2: occasional failures | 3: frequently flaky |
| N Nature | Worst case. It sets a minimum class. | 0: life goes on if it fails, maybe annoying | 1: it may cause a catastrophe | 2: imminent catastrophe if it fails |
| X Complexity | Effort anchor. It is stored, and used only for tickets. | 1: simple, anyone on the team can change it | 2: several business rules, or domain knowledge needed | 3: hard algorithm, or only one or two people can change it safely |
Two details are easy to miss. C takes the worse of its two halves, so a stateless hard dependency and an easy-to-replay but critical job both score high. And N describes the worst case, while I describes the typical case.
b) Ticket: the letters
A ticket inherits everything from its component. Only two inputs are new.
| Letter | Question | Values |
|---|---|---|
| M Manifestation | How much of the component's worst case does this bug cause? | Cosmetic ×0.25: no effect on the function, like a log typo. Partial ×0.5: an edge case, a workaround exists, or a small share of traffic. Core ×1: it hits the failure that made N high. |
| F Effort | How long will the fix take? | 1: under a day. 2: a few days. 3: a week or more. |
c) Equations, floors, guardrails and implicit rules
The component equations are simple arithmetic. The ticket equation adds the manifestation and the effort.
COMPONENT Consequence = I + C + E + B + L (0 to 14) Risk = Consequence × S (0 to 42) Class = max( band(Risk), floor(N) ) TICKET F_used = max( F_estimated, floor(X) ) (X = 3 means at least 2) Priority = Risk × M ÷ F_used The class comes from two lookups: the band of the Risk, and the floor from N. The higher result wins.
| Risk | Band |
|---|---|
| 0 to 7 | Low |
| 8 to 17 | Medium |
| 18 to 28 | High |
| 29 to 42 | Critical |
| N | Floor on the class |
|---|---|
| 2 | At least High |
| 1 | At least Medium |
| 0 | No floor, Risk decides |
Implicit rules at component level
- N is a floor, not part of the sum. "Imminent catastrophe" overlaps with I = 3 and C = 3, so adding it would count the same thing twice. As a floor, it also stops a very stable component from multiplying down to Medium.
- Multiplying by S removes trivial components. If the consequence is 0, Risk is 0, however flaky the component is.
- Watch your N = 2 count. If more than about 10 to 15 percent of your components are N = 2, the label has lost its meaning.
- S is reviewed on a schedule. Review it every quarter, or when bugs cluster. For a new component with no history, start from complexity: X = 3 starts at S = 2.
- X stays out of Risk and Class. Complexity already appears as likelihood through S, and a complex component is not more important only because it is complex.
- Key-person rule. X = 3 together with N of 1 or 2 means you need a second owner, documentation and a runbook.
Implicit rules at ticket level
- Class never changes between tickets of one component. Class says the component needs attention. Priority orders the work inside it.
- X floors the effort. A "one-line fix" in a complex component is rarely a one-hour job, because someone must load the context and test the rules.
- Drop-everything guardrail. If M is Core and N is 2, the ticket goes to the top, whatever the Priority. Dividing by effort is fine for normal work, but it must not push a data-corrupting bug behind quick wins.
- Effort never changes the class. A six-month fix on a core component is still High.
- A bug is evidence about S, not a change to S. Count bugs per component and use them in the quarterly review.
- Write the reason for M on the ticket. This stops authors from inflating M to jump the queue, and it gives reviewers something to challenge.
- Ticket to component mapping decides everything. A wrong mapping silently gives the wrong Risk. If the mapping is unclear, ask a human.
Part 3: Component evaluation example
Take a payment system built on Stripe with five components. The scorecard below gives each one a Class, and one of them shows the N floor at work.
| Component | I | C | E | B | L | Cons | S | Risk | N | Class | X |
|---|---|---|---|---|---|---|---|---|---|---|---|
| PaymentService | 3 | 3 | 3 | 3 | 2 | 14 | 1 | 14 | 2 | High (floor) | 3 |
| PaymentRetryJob | 2 | 2 | 2 | 2 | 1 | 9 | 2 | 18 | 1 | High | 2 |
| WebhookEventSender | 2 | 2 | 2 | 2 | 0 | 8 | 2 | 16 | 1 | Medium | 2 |
| Logging | 1 | 1 | 0 | 1 | 1 | 4 | 1 | 4 | 0 | Low | 1 |
| MetricsExporter | 0 | 0 | 0 | 0 | 0 | 0 | 2 | 0 | 0 | Low | 1 |
What the scorecard shows:
- PaymentService is very stable, so Risk is only 14, which is Medium. Because N = 2, the class rises to High. Without the floor, the most important component would sit in the same band as the webhook sender. It also has X = 3 and N of 2, so the key-person rule applies: it needs a second owner and a runbook.
- PaymentRetryJob is High on Risk alone (9 × 2 = 18). Stripe declines and rate limits make it flaky, which is why S is 2.
- Logging gets L = 1 because payment logs can matter in an audit. Nothing else keeps it above Low.
- MetricsExporter has a consequence of 0, so Risk is 0 even though S is 2. A flaky component that nobody needs cannot climb the bands.
Part 4: Ticket evaluation example
Now three bugs arrive in the same component, PaymentService. All three inherit Consequence 14, S = 1, Risk 14, N = 2, Class High and X = 3. Only M and F make them different. Because X = 3, the effort is never below 2.
| Bug | M | F estimated | F used | Calculation | Priority | Guardrail |
|---|---|---|---|---|---|---|
| A. Duplicate charge when a client retries after a Stripe timeout | Core ×1 | 1 | 2 | 14 × 1 ÷ 2 | 7.0 | Drop everything |
| B. 3-D Secure redirect fails for about 2% of cards, and the customer can retry | Partial ×0.5 | 3 | 3 | 14 × 0.5 ÷ 3 | 2.3 | None |
| C. Typo in the payment confirmation label | Cosmetic ×0.25 | 1 | 2 | 14 × 0.25 ÷ 2 | 1.8 | None |
Three things to notice:
- Bug A skips the ratio. M is Core and N is 2, so the guardrail puts it first. A cheap quick win must never be placed above a bug that charges customers twice.
- The X floor changes the order of B and C. Bug C looks like a one-hour fix, but the floor sets F to 2, so its priority is 1.8 and not 3.5. Without the floor, a typo would jump ahead of a real customer-facing defect.
- The class stays High for all three. Class tells you the component needs attention. Priority tells you the order of the work inside it.
Part 5: Using AI to apply the framework
You can automate most of the scoring, but the AI agent should only propose the inputs. Ordinary code should compute every number, because the formulas are plain arithmetic. This keeps results repeatable, and it makes the guardrail a hard-coded if, not something a model might forget.
What an agent can score well
| Letter | How much to trust the agent | Evidence it can use |
|---|---|---|
| C, E | High | Dependency graph, traffic, number of consumers, public or internal endpoints |
| S | High, but compute it | Incident history, error rates and past bug tickets per component |
| X | Medium | Number of business-rule branches, how many people changed the code |
| I | Medium | Code and docs, but worst-case thinking needs a review |
| B, L, N | Low | It can suggest with cited evidence, but a human decides |
| M | Good fit | Ticket text plus component description, with a one-line reason |
| F | Weak | Better to use the cycle time of similar past tickets, floored by X |
A possible setup
- Keep a component registry in git, with the scores, the evidence, the owner and the last review date.
- Run a scheduled agent every quarter, and when bugs cluster. It opens a pull request with proposed score changes and evidence. A human reviews and merges it.
- When a ticket arrives, the agent maps it to a component and proposes M and F. A script then computes the Priority and writes it to a custom field on the ticket.
- Keep the formulas, bands, floors and the guardrail in code, not in the prompt.
Guardrails for the agent
- Put your score tables and examples in the prompt, and ask for a quote of the evidence behind every score. Without anchors, scores drift between runs.
- Score each component twice and compare. If the two runs disagree, the rubric is probably unclear.
- Start in shadow mode for a few sprints. Write the priority, but do not let it reorder anything, and compare it with how the team would rank the backlog.
- Show the reason for M on the ticket and keep a human override, because people can inflate M to jump the queue.
- Send tickets with a low-confidence component mapping to a person. A wrong mapping is worse than no score.
Part 6: What this model lacks
CEBIL is a triage tool, and it has real limits. You should know them before you adopt it.
- No time dimension. Cost of delay is about how loss grows over time, including fixed deadlines. CEBIL has no deadline, so a bug that becomes expensive in ten days looks the same as one that does not. If you need this, add a deadline flag that works like the guardrail.
- No confidence term. RICE reduces the score of items with uncertain estimates. In CEBIL, a wrong M or a wrong component mapping goes straight into the Priority. The human override and the escalation of unclear mappings are the only protection.
- No upside. The model measures only what can break. It cannot rank new features, so do not use it for that.
- Calibration is your work. RICE and WSJF have scales that many people already know. The anchors in this post are my own, so your team must test them on real components and adjust them.
- Multiplication can still hide a rare disaster. The N floor is the answer, but it depends on honest scoring of N, and honest scoring is hard.
A practical way to combine them: use RICE or WSJF for features, use CEBIL for bugs and reliability work, and feed a component's Risk into WSJF as the risk-reduction input when both kinds of work compete.
P.S. Put the CEBIL score in the code
I suggest adding a CEBIL comment at the top of the main file of each component, with the date of the last review. Then the risk is visible exactly where people make changes, and a reviewer can see how old the score is.
/** * CEBIL - reviewed 2026-11-11 - next review 2027-02-11 * Component: PaymentService (Stripe charges) * I:3 C:3 E:3 B:3 L:2 -> Consequence 14 * S:1 N:2 X:3 -> Risk 14, Class High (N = 2 floor) * Owner: payments-team * Rules: Core bugs here are drop-everything. * Effort estimates start at 2 days or more (X = 3). * Changes need a second reviewer (N = 2). */ export class PaymentService { // ... } This small habit helps in four ways:
- Review scope. A pull request that touches a file with N = 2 can require a second reviewer, and a file with L = 2 can require an audit check.
- Honest dates. A score is a claim about the code, and code changes. A CI job can read the header and fail when the review date is older than 90 days.
- Feed for the agent. The same header can be the source of the component registry from Part 5, so scores live next to the code and not in a separate document that nobody opens.
- Faster onboarding. A new developer sees in ten seconds that this file is not a normal place for a quick fix.
If you try CEBIL, I would like to hear which scores felt wrong. Those are the anchors that need work.


