Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • How to Use This Course
  • Reading Paths
  • Concept Map
  • Meet DCRouter
  • Glossary
  • Part 0: What Verification Is8
  • Part 1: What Can Go Wrong11
  • Part 2: Specifications as Checkable Rules8
  • Part 3: Observability and Controllability7
  • Part 4: Stimulus11
  • Part 5: Checking11
  • Part 6: Coverage10
  • Part 7: Formal and Simulation10
  • Part 8: Verification Levels and Reuse8
  • Part 9: Planning and Risk9
  • Part 10: Emulation and Prototyping8
  • Part 11: Post-Silicon Validation8
  • Part 12: Bug Economics8
    • The Cost of Bugs by Stage
    • Bug-Discovery Curves
    • Diminishing Returns
    • Triage and Severity
    • Root Cause and Fix Quality
    • Estimating Remaining Bugs
    • Investing in Infrastructure
    • Drills: Bug Economics
  • Part 13: Debug7
  • Part 14: Sign-Off7
  • Part 15: Verification in Context8
  • Part 16: Case Studies5
  • Part 17: Capstones5
  • Part 18: Interview Preparation6
Loading lesson

‹Part 12: Bug Economics
  1. Topics
  2. First Principles of Verification Strategy
  3. Part 12: Bug Economics
  4. Bug-Discovery Curves

Part 12 · Bug Economics · Senior (S)

Bug-Discovery Curves

Bugs found per week rise as the environment comes up, peak, and fall into a long tail. A reliability-growth model fitted to the weekly counts estimates how many bugs this kind of testing will eventually find, and so how many are left. The fit, run on a simulated project and on 300 more, shows both the use and the uncertainty: estimates centre near the truth but spread widely, especially early.

On this page

    Note

    Level: Senior (S) · Answers: How do we spend verification effort wisely?

    Before this: Cost of Bugs by Stage, Metrics and Their Traps

    Every Monday the verification lead plots the bugs found per week. For the first weeks the line climbs as the testbench comes up; then it peaks; for the last two months it drifts down: 4, 4, 2, 3, 1, 0, 3. Management asks whether a falling curve means the design is nearly clean. It might. It might also mean the tests have stopped finding what is there (lesson 12.3). The curve needs a model to be read, and the model has error bars.

    What does a flattening bug curve mean, and how many bugs are left?

    The obvious answer, and why it breaks

    The obvious reading is "fewer bugs found means fewer bugs left". A falling curve has three possible causes: the design really has fewer bugs left, the current techniques have exhausted what they can find, or effort fell (people moved, the regression broke). A growth model can estimate the first under the assumption that the testing stays the same; it cannot tell the three apart by itself.

    The idea: fit a growth curve

    m(t)=a(1−e−bt)bugs found by week tremaining≈a^−found

    The Goel-Okumoto model (fact M7) assumes a pool of a bugs findable by this testing, each found at a constant rate b. Fitting a and b to the weekly counts estimates the pool and so the bugs still to find, assuming the testing does not change.

    Build it: fit a project, then 300 more

    A simulated project (true pool 80 bugs, rate 0.12 per week) is fitted at week 8 and week 16. Then 300 more projects with the same true curve show how much estimates vary from project to project.

    example.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    """Bug discovery curves: fitting a reliability-growth model to weekly bug counts.
    
    The Goel-Okumoto model says the expected number of bugs found by time t is
    m(t) = a (1 - exp(-b t)): a is the total number of bugs that testing of this kind will find,
    b how fast they are found (fact M7). A project (illustrative, a = 80, b = 0.12 per week) finds
    bugs week by week as Poisson counts. At week 8 and at week 16 the model is fitted to the counts
    so far by maximum likelihood, giving an estimate of a and so of the bugs still to find.
    Then 300 more simulated projects with the same true curve show how much such estimates spread.
    """
    import math
    import random
    
    rng = random.Random(21)
    A_TRUE, B_TRUE, WEEKS = 80, 0.12, 16
    
    def poisson(lam):
        k, p, l = 0, 1.0, math.exp(-lam)
        while True:
            p *= rng.random()
            if p < l:
                return k
            k += 1
    
    m = lambda a, b, t: a * (1 - math.exp(-b * t))
    weekly = [poisson(m(A_TRUE, B_TRUE, w) - m(A_TRUE, B_TRUE, w - 1)) for w in range(1, WEEKS + 1)]
    
    def fit(counts):
        """Maximum likelihood for grouped Poisson counts; a has a closed form for each b."""
        T, n = len(counts), sum(counts)
        best = None
        for i in range(1, 1000):
            b = i / 2000
            a = n / (1 - math.exp(-b * T))
            ll = 0.0
            for w, c in enumerate(counts, start=1):
                lam = m(a, b, w) - m(a, b, w - 1)
                ll += c * math.log(lam) - lam
            if best is None or ll > best[0]:
                best = (ll, a, b)
        return best[1], best[2]
    
    print("week " + " ".join(f"{w:3d}" for w in range(1, WEEKS + 1)))
    print("bugs " + " ".join(f"{c:3d}" for c in weekly))
    for upto in (8, 16):
        a, b = fit(weekly[:upto])
        found = sum(weekly[:upto])
        print(f"fit at week {upto:2d}: a = {a:5.1f}, b = {b:.3f}; found {found}, estimated still to find {a - found:5.1f}")
    a16, b16 = fit(weekly)
    fitted = [m(a16, b16, w) - m(a16, b16, w - 1) for w in range(1, WEEKS + 1)]
    print("fit  " + " ".join(f"{f:3.0f}" for f in fitted))
    
    def spread(upto, runs=300):
        est = []
        for _ in range(runs):
            counts = [poisson(m(A_TRUE, B_TRUE, w) - m(A_TRUE, B_TRUE, w - 1)) for w in range(1, upto + 1)]
            est.append(fit(counts)[0])
        est.sort()
        return est[runs // 10], est[runs // 2], est[9 * runs // 10]
    s8, s16 = spread(8), spread(16)
    print(f"300 projects, estimated total at week  8: median {s8[1]:5.1f}, 10-90% range {s8[0]:5.1f} to {s8[2]:5.1f}")
    print(f"300 projects, estimated total at week 16: median {s16[1]:5.1f}, 10-90% range {s16[0]:5.1f} to {s16[2]:5.1f}")
    assert (s16[2] - s16[0]) < (s8[2] - s8[0]) and abs(s16[1] - A_TRUE) < 10
    print(f"PASS: estimates centre near the true {A_TRUE} but spread widely; the range narrows from week 8 to week 16")

    It prints:

    Diagram
    week   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16
    bugs  10   6   7   7   3  10   6   4   4   4   2   3   1   0   3   6
    fit at week  8: a = 127.7, b = 0.067; found 53, estimated still to find  74.7
    fit at week 16: a = 100.4, b = 0.088; found 76, estimated still to find  24.4
    fit    8   8   7   7   6   5   5   5   4   4   4   3   3   3   2   2
    300 projects, estimated total at week  8: median  84.5, 10-90% range  57.1 to 200.3
    300 projects, estimated total at week 16: median  81.8, 10-90% range  68.4 to  97.7
    PASS: estimates centre near the true 80 but spread widely; the range narrows from week 8 to week 16
    The model's numbers: weekly counts from 10 down to between 0 and 6, with the fitted curve falling smoothly from 8 to 2 bugs per week.

    How to read it: this project's week-16 fit estimates about 100 bugs in total, so about 24 still to find, when the truth is 80 with 4 left. The counts are noisy (a week with 6 bugs at the end pulls the estimate up). Across 300 projects the estimate is right on average, centring near 82, but the 10–90% range at week 16 is 68 to 98; at week 8 it is 57 to 200. A growth model gives a useful order of magnitude and a trend, not a number to sign off on.

    Reading a discovery curve

    Pattern

    Possible meanings

    Check

    Rising

    Environment coming up; new features enabled

    Expected early

    Peak then steady decline

    Pool being exhausted for current techniques

    Fit a growth model; compare effort

    Sudden drop

    Effort fell, regression broken, or a big fix

    Look at runs and effort that week

    Late spike

    New feature, new technique, or a change introduced bugs

    Look at what changed (lesson 9.7)

    Long flat tail near zero

    Technique exhausted, or design clean

    Try a different technique (lesson 12.3); estimate with capture-recapture (lesson 12.6)

    How real teams use discovery curves

    Practice

    Purpose

    Weekly bug counts by feature and by technique

    Where bugs still come from

    Growth-model fits with ranges, not single numbers

    Honest estimates of what is left

    Curves plotted with effort and changes

    Falling rates interpreted correctly

    Discovery-curve criteria in sign-off (lesson 14.2)

    A rate below a threshold for several weeks, with effort sustained

    What breaks

    A team (illustrative) signed off when its bug curve reached zero for two weeks. Those two weeks were the weeks the regression farm was moved to a new data centre and ran a fraction of its usual tests. A curve read without the effort beside it measured the move, not the design.

    Why this matters

    Principal lens. Read discovery curves with effort and change history beside them, fit growth models with their ranges, and never treat a single fitted number as the count of remaining bugs. A falling curve is a question about the techniques as much as about the design.

    Interview

    Asked: "What does a flattening bug curve mean?"

    Weak answer: That the design is almost bug-free.

    Strong answer: One of three things: fewer bugs are left, the current techniques have stopped finding what's there, or effort dropped. I'd check effort and changes first, then fit a growth model such as Goel-Okumoto to estimate the pool for this kind of testing, with its range, because the estimates are noisy: in a simulation with 80 true bugs, fits at week 16 ranged from about 68 to 98 across projects, and at week 8 up to 200. If the curve is flat with effort sustained, I'd try a different technique before concluding the design is clean.

    Follow-ups: (1) What does a growth model assume? (2) How else can you estimate remaining bugs?

    Check yourself Why does the single project's week-16 estimate (about 100) differ so much from the truth (80)?

    Weekly counts are random; a late week with 6 bugs makes the curve look less exhausted than it is, pulling the fitted total up.

    Check yourself What does a growth model assume that can make it wrong in a real project?

    That the testing stays the same; a new technique, new features or a change in effort breaks the assumption.

    Key takeaways

    • Discovery curves rise, peak and tail; a falling curve has several possible causes.

    • Growth models estimate the pool of bugs findable by current testing, with wide uncertainty early.

    • Read curves with effort and changes beside them, and report estimates as ranges.

    Keep going

    Next: Lesson 12.3, Diminishing Returns. When the next bug costs too much, change technique.

    Related topics

    • Part 12 overview: Bug Economics

    Previous

    116 / 160

    Next: Diminishing Returns

    Up next

    Up nextDiminishing Returns

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Triage and Severity
    • Root Cause and Fix Quality
    • Estimating Remaining Bugs