Files
nexus/sreweekly/articles/423/04-rice-s-theorem-and-software-failures.html
2026-09-12 17:23:01 +08:00

459 lines
26 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<title>Rice&#x27;s Theorem and Software Failures</title>
<meta name="HandheldFriendly" content="True" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" type="text/css" href="https://blog.relyabilit.ie/assets/css/style.css?v=QmDy475QI3K1foi7" />
<script>
var siteUrl = 'https://blog.relyabilit.ie';
</script>
<script>
var localTheme = localStorage.getItem('attila_theme');
switch (localTheme) {
case 'dark':
document.documentElement.classList.add('theme-dark');
break;
case 'light':
document.documentElement.classList.add('theme-light');
break;
default:
break;
}
</script>
<style>
.theme-dark:root {
--ghost-accent-color: #ff6633;
}
@media (prefers-color-scheme: dark) {
html:not(.theme-light):root {
--ghost-accent-color: #ff6633;
}
}
</style>
<meta name="description" content="Software cannot be shown to be stable, and so it’s safer to assume it isn’t.">
<link rel="icon" href="https://storage.ghost.io/c/35/8b/358be036-5da7-4000-beff-df259098e18a/content/images/2021/09/App-Amethyst.ico" type="image/x-icon">
<link rel="canonical" href="https://blog.relyabilit.ie/rices-theorem-and-software-failures/">
<meta name="referrer" content="no-referrer-when-downgrade">
<meta property="og:site_name" content="RelyAbility Blog">
<meta property="og:type" content="article">
<meta property="og:title" content="Rice&#x27;s Theorem and Software Failures">
<meta property="og:description" content="Software cannot be shown to be stable, and so it’s safer to assume it isn’t.">
<meta property="og:url" content="https://blog.relyabilit.ie/rices-theorem-and-software-failures/">
<meta property="og:image" content="https://storage.ghost.io/c/35/8b/358be036-5da7-4000-beff-df259098e18a/content/images/size/w1200/2024/04/wolfgang-hasselmann-uYVXADKKJKI-unsplash.jpg">
<meta property="article:published_time" content="2024-04-09T23:32:56.000Z">
<meta property="article:modified_time" content="2024-04-10T14:50:39.000Z">
<meta property="article:tag" content="incidents">
<meta property="article:tag" content="models">
<meta property="article:publisher" content="https://www.facebook.com/ghost">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Rice&#x27;s Theorem and Software Failures">
<meta name="twitter:description" content="Software cannot be shown to be stable, and so it’s safer to assume it isn’t.">
<meta name="twitter:url" content="https://blog.relyabilit.ie/rices-theorem-and-software-failures/">
<meta name="twitter:image" content="https://storage.ghost.io/c/35/8b/358be036-5da7-4000-beff-df259098e18a/content/images/size/w1200/2024/04/wolfgang-hasselmann-uYVXADKKJKI-unsplash.jpg">
<meta name="twitter:label1" content="Written by">
<meta name="twitter:data1" content="Niall Murphy">
<meta name="twitter:label2" content="Filed under">
<meta name="twitter:data2" content="incidents, models">
<meta name="twitter:site" content="@niallm">
<meta name="twitter:creator" content="@niallm">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="800">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"publisher": {
"@type": "Organization",
"name": "RelyAbility Blog",
"url": "https://blog.relyabilit.ie/",
"logo": {
"@type": "ImageObject",
"url": "https://storage.ghost.io/c/35/8b/358be036-5da7-4000-beff-df259098e18a/content/images/2021/09/Logo---Alternate-Layout---Black-2.png"
}
},
"author": {
"@type": "Person",
"name": "Niall Murphy",
"image": {
"@type": "ImageObject",
"url": "https://storage.ghost.io/c/35/8b/358be036-5da7-4000-beff-df259098e18a/content/images/2021/11/recent-headshot--2-.jpg",
"width": 622,
"height": 587
},
"url": "https://blog.relyabilit.ie/author/niallmurphy/",
"sameAs": [
"http://www.relyabilit.ie",
"https://x.com/niallm"
]
},
"headline": "Rice's Theorem and Software Failures",
"url": "https://blog.relyabilit.ie/rices-theorem-and-software-failures/",
"datePublished": "2024-04-09T23:32:56.000Z",
"dateModified": "2024-04-10T14:50:39.000Z",
"image": {
"@type": "ImageObject",
"url": "https://storage.ghost.io/c/35/8b/358be036-5da7-4000-beff-df259098e18a/content/images/size/w1200/2024/04/wolfgang-hasselmann-uYVXADKKJKI-unsplash.jpg",
"width": 1200,
"height": 800
},
"keywords": "incidents, models",
"description": "Software cannot be shown to be stable, and so it’s safer to assume it isn’t.",
"mainEntityOfPage": "https://blog.relyabilit.ie/rices-theorem-and-software-failures/"
}
</script>
<meta name="generator" content="Ghost 6.64">
<link rel="alternate" type="application/rss+xml" title="RelyAbility Blog" href="https://blog.relyabilit.ie/rss/">
<script defer src="https://cdn.jsdelivr.net/ghost/portal@~2.71/umd/portal.min.js" data-i18n="true" data-ghost="https://blog.relyabilit.ie/" data-key="b25ad4e06b5e0cae96b3fa8e63" data-api="https://niallmurphy.ghost.io/ghost/api/content/" data-locale="en" crossorigin="anonymous"></script><style id="gh-members-styles">.gh-post-upgrade-cta-content,
.gh-post-upgrade-cta {
display: flex;
flex-direction: column;
align-items: center;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
text-align: center;
width: 100%;
color: #ffffff;
font-size: 16px;
}
.gh-post-upgrade-cta-content {
border-radius: 8px;
padding: 40px 4vw;
}
.gh-post-upgrade-cta h2 {
color: #ffffff;
font-size: 28px;
letter-spacing: -0.2px;
margin: 0;
padding: 0;
}
.gh-post-upgrade-cta p {
margin: 20px 0 0;
padding: 0;
}
.gh-post-upgrade-cta small {
font-size: 16px;
letter-spacing: -0.2px;
}
.gh-post-upgrade-cta a {
color: #ffffff;
cursor: pointer;
font-weight: 500;
box-shadow: none;
text-decoration: underline;
}
.gh-post-upgrade-cta a:hover {
color: #ffffff;
opacity: 0.8;
box-shadow: none;
text-decoration: underline;
}
.gh-post-upgrade-cta a.gh-btn {
display: block;
background: #ffffff;
text-decoration: none;
margin: 28px 0 0;
padding: 8px 18px;
border-radius: 4px;
font-size: 16px;
font-weight: 600;
}
.gh-post-upgrade-cta a.gh-btn:hover {
opacity: 0.92;
}</style>
<script defer src="https://cdn.jsdelivr.net/ghost/sodo-search@~1.8/umd/sodo-search.min.js" data-key="b25ad4e06b5e0cae96b3fa8e63" data-styles="https://cdn.jsdelivr.net/ghost/sodo-search@~1.8/umd/main.css" data-sodo-search="https://niallmurphy.ghost.io/" data-locale="en" crossorigin="anonymous"></script>
<link href="https://blog.relyabilit.ie/webmentions/receive/" rel="webmention">
<script defer src="/public/cards.min.js?v=ShRHxgy4po8zN-Wf"></script>
<link rel="stylesheet" type="text/css" href="/public/cards.min.css?v=WwnU9jw5ancNC8Gc">
<script defer src="/public/member-attribution.min.js?v=AKG4hWena9j3yX3I"></script>
<script defer src="/public/ghost-stats.min.js?v=vFcCUf6ZQ0Hyhc8h" data-stringify-payload="false" data-datasource="analytics_events" data-storage="localStorage" data-host="https://blog.relyabilit.ie/.ghost/analytics/api/v1/page_hit" tb_site_uuid="358be036-5da7-4000-beff-df259098e18a" tb_post_uuid="507ee85c-b1a4-4a39-aad9-7552acc490d6" tb_post_type="post" tb_member_uuid="undefined" tb_member_status="undefined" tb_gift_link=""></script><style>:root {--ghost-accent-color: #b18cfe;}</style>
</head>
<body class="post-template tag-incidents tag-models">
<div class="nav-header">
<nav class="nav-wrapper" aria-label="Main">
<span class="logo">
<a href="https://blog.relyabilit.ie" title="Home"><img src="https://storage.ghost.io/c/35/8b/358be036-5da7-4000-beff-df259098e18a/content/images/2021/09/Logo---Alternate-Layout---Black-2.png" alt="Logo" /></a>
</span>
<ul>
<li class="nav-main-website"><a href="https://www.relyabilit.ie/"><span>Main Website</span></a></li>
<li class="nav-blog"><a href="https://blog.relyabilit.ie/"><span>Blog</span></a></li>
<li class="nav-help"><a href="https://ghost.org/docs/"><span>Help</span></a></li>
</ul>
<ul class="nav-meta">
<li class="nav-twitter">
<a aria-label="Twitter" href="https://x.com/niallm" title="@niallm" target="_blank">
<i class="icon icon-twitter" aria-hidden="true"></i>
<span>@niallm</span>
</a>
</li>
<li class="nav-facebook">
<a aria-label="Facebook" href="https://www.facebook.com/ghost" title="ghost" target="_blank">
<i class="icon icon-facebook" aria-hidden="true"></i>
<span>ghost</span>
</a>
</li>
<li class="nav-search" style="display: none;">
<a title="Search">
<i class="icon icon-search" aria-hidden="true"></i>
<span>Search</span>
</a>
</li>
<!--
<li class="nav-subscribe">
<a href="#/portal">Subscribe</a>
</li>
-->
</ul>
</nav>
<div class="nav-wrapper-control">
<div class="inner">
<a class="nav-menu" role="button"><i class="icon icon-menu" aria-hidden="true"></i>Menu</a>
<a class="nav-search" style="display: none;" title="Search" role="button"><i class="icon icon-search" aria-hidden="true"></i></a>
</div>
</div>
</div>
<div class="nav-close" role="button" aria-label="Close"></div>
<section class="page-wrapper">
<div class="progress-container">
<span class="progress-bar"></span>
</div>
<header class="post-header has-cover ">
<div class="inner">
<span class="post-info">
<span class="post-type">Article</span>
<span class="post-count">incidents</span>
</span>
<h1 class="post-title">Rice&#x27;s Theorem and Software Failures</h1>
<div class="post-meta">
<div class="post-meta-avatars">
<figure class="post-meta-avatar avatar">
<a href="/author/niallmurphy/" class="author-avatar">
<img class="author-profile-image" src="https://storage.ghost.io/c/35/8b/358be036-5da7-4000-beff-df259098e18a/content/images/2021/11/recent-headshot--2-.jpg" alt="Niall Murphy" />
</a>
</figure>
</div>
<h4 class="post-meta-author"><a href="/author/niallmurphy/">Niall Murphy</a></h4>
<time datetime="10-04-2024">10 Apr 2024</time> &bull; 4 min read
</div>
<div class="post-cover cover">
<img
srcset="https://storage.ghost.io/c/35/8b/358be036-5da7-4000-beff-df259098e18a/content/images/size/w320/2024/04/wolfgang-hasselmann-uYVXADKKJKI-unsplash.jpg 320w,
https://storage.ghost.io/c/35/8b/358be036-5da7-4000-beff-df259098e18a/content/images/size/w640/2024/04/wolfgang-hasselmann-uYVXADKKJKI-unsplash.jpg 640w,
https://storage.ghost.io/c/35/8b/358be036-5da7-4000-beff-df259098e18a/content/images/size/w960/2024/04/wolfgang-hasselmann-uYVXADKKJKI-unsplash.jpg 960w,
https://storage.ghost.io/c/35/8b/358be036-5da7-4000-beff-df259098e18a/content/images/size/w1920/2024/04/wolfgang-hasselmann-uYVXADKKJKI-unsplash.jpg 1920w"
src="https://storage.ghost.io/c/35/8b/358be036-5da7-4000-beff-df259098e18a/content/images/size/w1920/2024/04/wolfgang-hasselmann-uYVXADKKJKI-unsplash.jpg"
alt="Rice&#x27;s Theorem and Software Failures" />
</div>
</div>
</header>
<main class="content" role="main">
<article class="post tag-incidents tag-models">
<div class="inner">
<section class="post-content">
<p><strong>Mental Models of Software</strong></p><p>My corner of the industry spends a lot of time worrying about effective ways to understand software behaviour, especially in the context of distributed systems. One important sociological disjunction we see when we try to improve our understanding is the disjunction between - I put it loosely - management and workers (in particular, incident responders) and what they see as the underlying basis for software failure.</p><p>Again, this is approximate, but let me characterise two views of the world.</p><p>In view A, software runs stably unless acted on by an external force, and that force is overwhelmingly human-originated. Therefore, when something goes wrong with the software, it’s overwhelmingly likely to be a human who did something wrong. Restoration of service is a task of reverting the human-originated change, and prevention of error is closely tied with preventing humans doing “bad things”. (There are many possible critiques of this view; I merely relate the core proposition.)</p><p>In view B, software is capricious and unstable-by-default, the environment is constantly changing, and if there is a failure, it can be the result of the combination of a collection of side-effects from a thousand different actions that are causally very distant from each other. It is therefore not useful to say a human is at fault. (Critiques of this view are also available: for example, the notion that for deterministic software, at some root of some chain must be a human action.)</p><p>It is important to say that these views are not pure logical opposites, and could both plausibly be claimed to be wrong, but are distinct models about the operation of software that drive a view on how to attribute sources of failure in software (and how to prevent same, restore service, etc). They are roughly analogous to <a href="https://nick.groenen.me/notes/the-old-and-new-view-on-human-error/?ref=blog.relyabilit.ie" rel="noreferrer">Dekker's Old View versus New View</a> on human error.</p><p>For completeness, we would stereotypically say that management holds view A and the workers hold view B.</p><p><strong>Rice’s Theorem</strong></p><p>Enter computational theory.</p><p><a href="https://en.wikipedia.org/wiki/Rice%27s_theorem?ref=blog.relyabilit.ie" rel="noreferrer">Rice’s Theorem</a> is related to the <a href="https://en.wikipedia.org/wiki/Halting_problem?ref=blog.relyabilit.ie" rel="noreferrer">Halting Problem</a>, which we won’t itself specifically discuss here, except to say that it is hugely important and generally well covered in undergraduate Computer Science.</p><p>Rice’s Theorem, a generalization of the Halting Problem, states that all <em>non-trivial semantic</em> properties of programs are <em>undecidable</em>.</p><p><em>Non-trivial</em> in this case has a narrow, technical meaning: for something to be <em>trivial</em> (note the logical inversion) it means it is true for all programs or for no programs. Therefore, for something to be <em>non-trivial</em>, it means it is true for some subset of programs, and not for the complement set.</p><p><em>Semantic</em> means that it is about a program’s meaning or behaviour, rather than its syntax. For example, the property <em>halts for all even inputs</em> is a semantic property, but <em>program text contains</em> </p><p><code>while (x != true) { continue; }</code></p>
<p>is a syntactic property.</p><p><em>Undecidable</em> has a number of definitions available, but for the purposes of this article we would say “cannot be correctly evaluated to be either true or false by a program”. We note here that undecidability doesn’t mean that the proposition is neither true nor false - just that it cannot be established to be so by the operation of some definite algorithm.</p><p>There are an uncountably<em> </em>infinite number of things which are undecidable. Some famous examples amongst mathematicians would be the continuum hypothesis and the axiom of choice. But it turns out that almost everything useful that you’d want to know about software is also undecidable. Wikipedia outlines the following propositions (with quoted preamble):</p><blockquote><em>Given a program P which takes a natural number n and returns a natural number P(n), the following questions are undecidable:</em></blockquote><ul><li><em>Does P terminate on a given n?</em></li><li><em>Does P terminate on 0?</em></li><li><em>Does P terminate on all n?</em></li><li><em>Does P terminate and return 0 on every input?</em></li><li><em>Does P terminate and return 0 on some input?</em></li><li><em>Does P terminate and return the same value for all inputs?</em></li><li><em>Is P equivalent to a given program Q?</em></li></ul><p><strong>Relevance to software failures&nbsp;</strong></p><p>It seems clear that we can extend this fairly naturally to cover the software we work with today, and therefore the relevance to software failures (and therefore incidents) is direct. <em>To wit, we cannot know for sure whether or not software is going to be incident-free.</em> <em>It might well be, but we can’t ever prove it.</em></p><p>(The analogy I like, though I know of no theoretical basis for it, is that sufficiently complex systems appear to behave more like a “generative grammar” for incidents, producing novel problems from combinations of situations in the absolutely vast potential state space of software.)</p><p>Of course, undecidability results are satisfyingly broad from a rhetorical point of view - they state that literally <em>no </em>program/algorithm can do X - but unsatisfyingly shallow from a behavioural description point of view.</p><p>You can’t prove a program halts, sure, but you <em>can</em> make a lot of progress in reasoning about program behaviour outside of halting. (Fascinatingly, Rice’s theorem suggests that the real hope here is in syntactical models for this, rather than semantic ones.)</p><p><strong>Relevance to mental models&nbsp;</strong></p><p>But it’s precisely that combination of broad and shallow which is useful here.</p><p>The argument uses a similar inversion to the Halting problem itself.</p><p>Let us suppose that there <em>was</em> a way to prove that a program would work without errors. Then View A of software would be correct, since by definition, you have proved that the software works for some input X, so if a software failure results, it must be for some reason outside of the combination of code plus input, which is (in View A terms) human action resulting in some change such that the previous proof is no longer valid.</p><p>But there isn’t a way to prove a program will work without errors. Therefore View A can’t be correct. Whether or not View B is the correct one instead is unclear. But if View A can’t be adopted, View B is one potential substitute.</p><p><strong>Conclusion</strong></p><p>We need to help holders of view A understand that it is not true, and that their model that the software is inherently stable is incorrect. To the extent it’s useful, it is therefore possible, and true, to say to leaders <em>“software cannot be shown to be stable, and so it’s safer to assume it isn’t”.</em></p>
</section>
<section class="post-footer">
<div class="post-share">
<span class="post-info-label">Share</span>
<a title="Twitter" aria-label="Twitter" class="twitter" href="https://twitter.com/share?text=Rice&#x27;s Theorem and Software Failures&url=https://blog.relyabilit.ie/rices-theorem-and-software-failures/" onclick="window.open(this.href, 'twitter-share', 'width=550,height=235');return false;">
<i class="icon icon-twitter" aria-hidden="true"></i>
</a>
<a title="Facebook" aria-label="Facebook" class="facebook" href="https://www.facebook.com/sharer/sharer.php?u=https://blog.relyabilit.ie/rices-theorem-and-software-failures/" onclick="window.open(this.href, 'facebook-share','width=580,height=296');return false;">
<i class="icon icon-facebook" aria-hidden="true"></i>
</a>
<a title="LinkedIn" aria-label="LinkedIn" class="linkedin" href="https://www.linkedin.com/shareArticle?mini=true&amp;url=https://blog.relyabilit.ie/rices-theorem-and-software-failures//&amp;title=Rice&#x27;s Theorem and Software Failures" onclick="window.open(this.href, 'linkedin-share', 'width=930,height=720');return false;">
<i class="icon icon-linkedin" aria-hidden="true"></i>
</a>
<a title="Email" aria-label="Email" class="email" href="mailto:?subject=Rice&#x27;s Theorem and Software Failures&amp;body=https://blog.relyabilit.ie/rices-theorem-and-software-failures/">
<i class="icon icon-mail" aria-hidden="true"></i>
</a>
</div>
<aside class="post-tags">
<span class="post-info-label">Topic</span>
<a href="/tag/incidents/">incidents</a> <a href="/tag/models/">models</a>
</aside>
</section>
<div id="commento"></div>
<script defer
src="https://cdn.commento.io/js/commento.js">
</script>
<aside class="post-nav">
<a class="post-nav-next" href="/detecting-disturbance-incidents-and-benfords-law/">
<section class="post-nav-teaser">
<i class="icon icon-arrow-left" aria-label="Next post"></i>
<h2 class="post-nav-title">Detecting Disturbance: incidents and Benford&#x27;s Law</h2>
<p class="post-nav-excerpt">Recently we at Stanza have been exploring operational data, and it&#39;&hellip;</p>
<p class="post-nav-meta"><time datetime="13-08-2024">13 Aug 2024</time></p>
</section>
</a>
<a class="post-nav-prev" href="/graceful-degradation-and-slos/">
<section class="post-nav-teaser">
<i class="icon icon-arrow-right" aria-label="Previous post"></i>
<h2 class="post-nav-title">Graceful Degradation and SLOs</h2>
<p class="post-nav-excerpt">What is graceful degradation?
Graceful degradation is the idea that, when you&hellip;</p>
<p class="post-nav-meta"><time datetime="09-04-2024">09 Apr 2024</time></p>
</section>
</a>
<div class="clear"></div>
</aside>
</div>
</article>
</main>
<div class="search-wrapper">
<div class="search">
<form class="search-form">
<input class="search-field" type="text" placeholder="Search …">
<button class="search-button" type="submit">
<i class="icon icon-search" aria-hidden="true"></i>
</button>
</form>
<div class="popular-wrapper">
<h4 class="popular-title">Topics</h4>
<span class="popular-tags post-tags">
<a href='/tag/models/'>models: 6</a>
<a href='/tag/sre-identity/'>sre-identity: 4</a>
<a href='/tag/slos/'>SLOs: 4</a>
<a href='/tag/organizational/'>organizational: 3</a>
<a href='/tag/incidents/'>incidents: 3</a>
<a href='/tag/planning/'>planning: 2</a>
<a href='/tag/future/'>future: 2</a>
<a href='/tag/gatekeeping/'>gatekeeping: 1</a>
<a href='/tag/horizontal/'>horizontal: 1</a>
<a href='/tag/is-not/'>is-not: 1</a>
<a href='/tag/legibility/'>legibility: 1</a>
<a href='/tag/okrs/'>okrs: 1</a>
<a href='/tag/systems-thinking/'>systems-thinking: 1</a>
<a href='/tag/google/'>google: 1</a>
</span>
</div>
<div class="search-result"></div>
</div>
<button class="search-wrapper-close" aria-label="Close"></button>
</div>
<div class="nav-footer">
<nav class="nav-wrapper" aria-label="Footer">
<span class="nav-copy">RelyAbility Blog &copy; 2026 <a class="nav-rss" title="RSS" href="https://blog.relyabilit.ie/rss/" target="_blank"><i class="icon icon-rss" aria-hidden="true"></i></a></span>
<span class="nav-credits">Published with <a href="https://ghost.org">Ghost</a> &bull; Theme <a href="https://github.com/zutrinken/attila">Attila</a> &bull; <a class="menu-item js-theme" href="#" data-system="System theme" data-dark="Dark theme" data-light="Light theme"><span class="theme-icon"></span><span class="theme-text">System theme</span> </a> </span>
</nav>
</div>
</section>
<script type="text/javascript" src="https://blog.relyabilit.ie/assets/js/script.js?v=GM11IFKYlUnuOcir"></script>
<script>
$(document).ready(function () {
var viewport = $(window);
var post = $('.post-content');
// Responsive videos with fitVids
post.fitVids();
// Format code blocks and add line numbers
function codestyling() {
$('pre code').each(function(i, e) {
// Code highlight
hljs.highlightBlock(e);
// No lines for plain text blocks
if (!$(this).hasClass('language-text')) {
var code = $(this);
// Calculate amount of lines
var lines = code.html().split(/\n(?!$)/g).length;
var numbers = [];
if (lines > 1) {
lines++;
}
for (i = 1; i < lines; i++) {
numbers += '<span class="line" aria-hidden="true">' + i + '</span>';
}
code.parent().append('<div class="lines">' + numbers + '</div>');
}
});
}
codestyling();
// Reading progress bar on window top
function readingProgress() {
var postBottom = post.offset().top + post.height();
var viewportHeight = viewport.height();
var progress = 100 - (((postBottom - (viewport.scrollTop() + viewportHeight) + viewportHeight / 3) / (postBottom - viewportHeight + viewportHeight / 3)) * 100);
$('.progress-bar').css('width', progress + '%');
(progress > 100) ? $('.progress-container').addClass('complete'): $('.progress-container').removeClass('complete');
}
readingProgress();
// Trigger reading progress
viewport.on({
'scroll': function() {
readingProgress();
},
'resize': function() {
readingProgress();
},
'orientationchange': function() {
readingProgress();
}
});
});
</script>
<!-- 100% privacy friendly analytics -->
<script async defer src="https://scripts.simpleanalyticscdn.com/latest.js"></script>
<noscript><img src="https://queue.simpleanalyticscdn.com/noscript.gif" alt="" referrerpolicy="no-referrer-when-downgrade" /></noscript>
</body>
</html>