Press two blocks of polished steel together as hard as you can. They look like they’re touching everywhere. They are not. They are touching at maybe twelve points — microscopic peaks called asperities — and those twelve points are carrying 100% of the load. The rest of the surface is scenery.
This is not a metaphor. This is what Frank Philip Bowden and David Tabor proved at Oxford in the 1950s, in a sequence of experiments most engineers still get wrong. And it is the thing your test coverage dashboard is hiding from you.
The Conductance Experiment
Bowden and Tabor’s setup was elegant. Pass a small electric current across two metal surfaces in contact. Current can only flow through metal-on-metal bridges, so the measured conductance is a direct readout of the real contact area. Now compare two configurations: a sphere resting on a flat (small apparent area), versus a flat resting on a flat (huge apparent area). If contact were what your eyes report, the flat-on-flat should conduct dramatically better.
It didn’t. The flat-on-flat conductance was only about twice the sphere-on-flat conductance. The “extra” apparent area was contributing almost nothing, because almost no metal was actually touching. For steel under modest pressure, the real contact area is on the order of 0.05% of the apparent area. The number you see is a thousand times larger than the number that matters.
Bowden and Tabor formalized this into A_r = N / p_y: the real contact area scales with the normal load divided by the yield pressure of the softer material. Friction, they argued in The Friction and Lubrication of Solids (Oxford, 1950 and 1964), is the shearing of cold-welded asperity junctions — not a property of the surface as a whole. The Amontons I result F = (τ/p_y)·N, taught in every freshman physics class, falls out of this microscopic accounting. Friction doesn’t care about the geometry your eyes report. It cares about the geometry under load.
Now look at your coverage dashboard. 95%. Tell me what is touching, and tell me what is scenery.
The Six Orders of k
The deeper lesson, and the one this essay is really about, isn’t Bowden and Tabor’s. It’s John Archard’s, from 1953. Archard wrote down what is still the standard wear law: V = k · (N · s) / H, where V is the volume of material worn away, N is normal load, s is sliding distance, and H is the hardness of the softer material. The dimensionless k is the wear coefficient.
Across real engineering surfaces, k spans roughly 10-8 to 10-2 — six orders of magnitude. That is the difference between a bearing that lasts a century and one that destroys itself before lunch. The same formula. The same materials, sometimes. Six orders of magnitude.
What changes? The regime. k has a physical interpretation as the probability that an asperity encounter produces a wear particle, and that probability depends on whether you’re in mild oxidative wear, adhesive wear, delamination, severe abrasion, or tribochemical attack. A 2025 review in MDPI Encyclopedia (Vol. 5, Issue 3, Article 124) and a contemporary data-driven evaluation in ASME Applied Mechanics Reviews (Vol. 77, Issue 2, 2025) both reach the same uncomfortable conclusion: Archard-type laws capture sliding wear to within a factor of two or three within a regime, and fail by orders of magnitude across regime transitions. The transitions are not gradual. They are cliffs. Increase load past a threshold and the dominant mechanism flips from oxidative to delamination to plastic deformation, with k jumping discontinuously each time.
You cannot compute k from material properties alone. Modern FEM simulations bake in an “initial wear stage” with one set of exponents and a “stable wear stage” with another, but the switch point has to be calibrated empirically. The formula is universal. The number you plug in for k is not.
Now hold that thought and look at your coverage dashboard again. One number. One regime-blind number. Reported as if a single digit could summarize a surface that has six orders of magnitude of variation hiding inside it.
The Friday Night
A fintech payments team had 92% unit test coverage. They felt good about it — 92% is the kind of number that ends meetings. They deployed on a Friday and locked thousands of merchants out of their accounts over the weekend. The post-mortem, summarized in a February 2026 write-up titled “The Code Coverage Lie,” found something quietly devastating: every failing line had been executed by tests. The bug wasn’t in the lines. It lived in the interaction across a service boundary under concurrent writes — a configuration the test suite had never loaded. (The post is anonymous and the company unnamed; treat the specific percentage as illustrative, the structural shape as common.)
This is the flat-on-flat conductance experiment in software form. The 92% number was apparent area. The actual asperity — the contended cross-service write — was a single junction the tests had never pressed.
A separate study, “Can We Trust Tests To Automate Dependency Updates?”, measured this directly. Tests detected only 47% of faults in direct dependencies and 35% in transitive ones, despite high nominal coverage. Same suite. Same coverage number. Same Archard formula. The detection rate dropped 25% just by moving one ring outward in the dependency graph. That isn’t a measurement of how good the tests are. That’s a measurement of how badly a single coverage number summarizes a multi-regime surface.
And the regime distribution is severe. The ISTQB defect-clustering principle — backed by decades of empirical observation across teams from banking to embedded systems — holds that roughly 80% of defects live in 20% of modules. A banking application example reported in the field showed 18 defects in a single “Overdraft” module out of 32 total across the system: 56% of defects concentrated in one component. The cause is consistent across reviews summarized cleanly in TestDevLab’s 2025 overview: complexity, intricate conditional logic, intertwined state, and change frequency are what create severe-regime conditions. The other 80% of modules are mild oxidative wear. The 20% that eat the defects are delamination.
If you report a single coverage number, you are summing wear coefficients across all those regimes and dividing by the surface area. The number you get is mathematically defensible and operationally meaningless.
The Measurement Tool Was Lying
Here is the part that should disturb you most. In 2024, Bart Weber and colleagues published a study in Journal of Physical Chemistry Letters (PMC 10895690) that pointed super-resolution fluorescence microscopy at a multi-asperity contact. They compared what conventional diffraction-limited imaging reported against what super-resolution actually saw.
For hard glass spheres pressed against a flat, conventional imaging reported a real contact area of 61 µm². Super-resolution reported 26 µm². Boundary-element simulations predicted 22 µm². The conventional measurement tool — the one tribologists had been using for decades — was over-reporting contact area by a factor of 2.4. Not because anyone was lying. Because the resolution of the tool wasn’t fine enough to distinguish illuminated scenery from load-bearing contact. Light scattered from regions that looked in contact were not actually carrying load.
For soft PMMA polymer spheres, the discrepancy collapsed to about 12%. Soft materials deform to create larger, more visible patches; the illusion of full contact comes closer to the reality. Stiff materials look misleadingly well-connected.
This is the diagonal punchline of the whole story. Line coverage tools have the same resolution problem. They cannot distinguish “a line was reached during a test” from “a line was loaded under the conditions that produce production failure.” The tool reports illumination, not load. For “stiff” code — deterministic, synchronous, well-structured — the over-reporting is dramatic, because the line is reached but the asperity geometry that matters (concurrent state, ordering, retry behavior, downstream rate limits) was never present in the test fixture. For “soft” code — flexible, late-binding, the kind that warps gracefully — the discrepancy shrinks, because there is less hidden state for the tool to miss.
You haven’t been lied to. The tool isn’t fine enough to tell you the truth.
The Diagnostic Move
The fix, from the tribology side, is the prescription Ian Hutchings and Phil Shipway lay out in chapter 11 of Tribology: Friction and Wear of Engineering Materials (2nd ed., Butterworth-Heinemann, 2017). Don’t report a single wear number. Build a wear map: plot mechanism against load and speed, identify the regime boundaries, and report behavior by regime. Engineers stopped trying to summarize wear with one scalar decades ago, because the scalar lied. The map tells the truth.
The software translation is direct. Stop reporting coverage as a number. Report it as a matrix:
- Cold paths — error handlers, migration scripts, dead-letter queues, the code that runs when the world breaks. Coverage X%, mutation score Y%, production invocations per day Z.
- Warm paths — regular request handling under normal traffic, the well-trodden middle. X / Y / Z.
- Hot paths — the request handlers serving the top 1% of traffic, the loops that dominate the flame graph. X / Y / Z.
- Contended paths — concurrent access, shared state, distributed coordination, anywhere ordering matters. X / Y / Z.
This decomposition surfaces what a single number hides. Cold paths often have 100% line coverage and 5% mutation score — they were touched once by an integration test and never again. Hot paths might have 80% coverage and 60% mutation score — closer to real contact, because production load already exercised them. Contended paths frequently have the highest coverage and the lowest real testing effectiveness, because concurrency bugs are precisely the asperities the unit-testing tool cannot see.
There is empirical support for this approach in the wild. A 2024 experience report from a Brazilian fintech (XXIII Brazilian Symposium on Software Quality, ACM DL 10.1145/3701625.3701629) found that significant improvement in mutation scores reduced production issues — but only when mutation testing was targeted at critical business logic, not applied uniformly across the codebase. They built a risk-based map: comprehensive operator coverage on hot-path business logic, focused operator subsets on utility code. They allocated testing intensity by regime, not by surface area. It worked.
A 2024 paper at ACM/IEEE AST (10.1145/3644032.3644442) makes the same point with a beautifully pointed title: “Mutation Coverage is not Strongly Correlated with Mutation Coverage.” The same test suite produces vastly different mutation coverage across different code regions. The metric is regime-dependent. That isn’t a flaw in the metric. That’s the metric finally telling the truth.
The Quantitative Prediction
The wear-coefficient analogy makes a specific, falsifiable claim: mature systems should exhibit roughly six orders of magnitude of variation in defect density across regions with identical nominal coverage. Stable utility code — string formatters, date parsers, anything pure and well-bounded — sits at something like 0.001 defects per KLOC at 95% coverage. Complex stateful business logic at 95% coverage runs closer to a few defects per KLOC. Concurrent distributed coordination code at 95% coverage routinely produces tens of defects per KLOC, sometimes hundreds.
That’s the k spectrum. Same formula. Same coverage number. Different regime, different probability per asperity encounter that the test suite catches the wear particle before production does. The variation is real and is the central reason that the single-number coverage metric is misleading rather than merely imprecise.
There is a softer caveat worth saying out loud: the six-orders claim mirrors the tribology range, and the defect-density data supporting it is patchier than the wear-coefficient data. Treat it as the analogy’s prediction, not as established empirical fact. The point isn’t to pin the exponent at exactly six. The point is that defect density across identically-covered code is wildly heteroscedastic, and the regime-stratified frame is how you make that visible.
Where the Analogy Bends
Three honest concessions.
First, code isn’t metal. Software contact is logical and stateful, not mechanical, and there are wear mechanisms in code — semantic drift, dependency rot, prompt-injection surface, alignment regressions — that have no clean tribological analogue. Tribochemistry is real, but you cannot push the metaphor through every failure mode.
Second, coverage isn’t useless. Low coverage is a strong negative signal — code paths that have never been exercised by tests are guaranteed to be untested. The argument here is that high coverage is a weak positive signal, not that coverage should be abandoned. The Bowden-Tabor reform of friction didn’t throw out the Amontons law; it explained when it works and when it doesn’t.
Third, regime-stratified reporting costs something. You need invocation data from production (distributed tracing, flame graphs, or at least request logs), you need to classify code paths by load profile, and you need to maintain that classification as the architecture drifts. None of this is free. The good news is that the cost is bounded — a few sessions of instrumentation, a recurring classification pass, and a dashboard rebuild. It is dramatically cheaper than a Friday-night merchant lockout.
What to Do on Monday
One concrete sequence:
- Pick the part of the system you most fear deploying on a Friday. That’s your contended-regime candidate.
- Pull a week of production telemetry. Tag every code path by invocation rate (cold / warm / hot) and concurrency profile (independent / contended).
- Re-run your coverage tooling, but slice the report by tag. The number you get for hot-contended paths is the only one that should affect your deployment confidence.
- For the hot-contended slice, add mutation testing. Mutation score on this slice is your real-contact metric. Published industrial experience (Google’s mutation testing program, among others) finds roughly 70% of real bugs have a corresponding mutant; that is far closer to load-bearing than line coverage will ever be.
- Stop reporting a single coverage number to leadership. Report the matrix. Watch what changes when stakeholders see the cold-path 100% next to the contended-path 40%.
The twelve asperities under those polished steel blocks carry 100% of the load. The rest of the surface is scenery — illuminated by your light source, but bearing nothing. Your test suite has its own twelve asperities, and right now your dashboard isn’t telling you which ones they are. Build the map. Find the points. Stop measuring scenery.
Sources: Bowden & Tabor, The Friction and Lubrication of Solids (Oxford, 1950 and 1964); Archard, “Contact and Rubbing of Flat Surfaces,” Journal of Applied Physics, 1953; MDPI Encyclopedia Vol. 5, Issue 3, Article 124, 2025 (tribology review); ASME Applied Mechanics Reviews Vol. 77, Issue 2, 2025 (data-driven wear-law evaluation); Weber et al., Journal of Physical Chemistry Letters, 2024 (PMC 10895690); Hutchings & Shipway, Tribology: Friction and Wear of Engineering Materials, 2nd ed. (Butterworth-Heinemann, 2017); “The Code Coverage Lie” (anonymous February 2026 write-up); “Can We Trust Tests To Automate Dependency Updates?” (study cited in source); ISTQB defect-clustering principle (industry standard, multiple sources); TestDevLab 2025 overview of defect distribution; XXIII Brazilian Symposium on Software Quality, ACM DL 10.1145/3701625.3701629, 2024; ACM/IEEE AST 2024 paper, 10.1145/3644032.3644442, “Mutation Coverage is not Strongly Correlated with Mutation Coverage”; Google mutation testing program (industrial experience report).
Build the Map. Find the Points.
The essay’s prescription is regime-stratified reporting: a per-execution receipt of what was tested, under what concurrency profile, against what load, with what mutation-score outcome — instead of a single line-coverage number averaging over the whole surface. That is what a provenance chain is for. Chain of Consciousness is an append-only signed record of what your tests and your production code actually did, sliceable by tag, queryable by regime — the substrate the wear map runs on.
pip install chain-of-consciousness
npm install chain-of-consciousness
Hosted Chain of Consciousness ships the audit trail as a service. The dashboard stops reporting illumination and starts reporting load.