{
    "componentChunkName": "component---src-templates-post-page-js",
    "path": "/2019/error-handling-introduction",
    "result": {"data":{"site":{"siteMetadata":{"title":"Solid Abstractions","siteUrl":"https://solidabstractions.com","twitterId":291334023,"author":{"fullName":"Julien Hartmann","profileHtml":"I am an open-source de­vel­op­er, for­mer IT con­sul­tant with a pas­sion for new tech­nol­o­gies. I be­lieve the role of an en­gi­neer is to em­pow­er peo­ple, by as­sem­bling sim­ple, re­fined de­signs.\n","links":[{"url":"https://github.com/spectras/","name":"github","title":"GitHub"},{"url":"https://stackoverflow.com/users/3212865/spectras","name":"stackoverflow","title":"StackOverflow"},{"url":"https://www.linkedin.com/in/julienhartmann/","name":"linkedin","title":"LinkedIn"}],"profilePicNode":{"original":{"src":"/static/profile-pic-301a9cbe7b572c3e7910c9717d2b3bcd.jpg"}},"url":"https://etherdream.org/about"}}},"markdownRemark":{"id":"02c2797f-c769-5acf-8116-5d4bbd0e0df1","excerpt":"","html":"<ol><li>→ Get the main functionnality working.</li>\n<li>→ Realize that anything unexpected crashes and burns.</li>\n<li>→ Add some band-aid where it hurts the most.</li></ol>\nIn this series, we cover the basics of correct error handling,\nso it is no longer an afterthought.\n<p>--- excerpt ---</p>\n<p>Error handling is a recurrent issue in beginners' and not-so-beginners' code.\nIt is often an afterthought. The typical flow goes like this:</p>\n<ul>\n<li>Get the main functionnality working.</li>\n<li>Realize that edge cases and anything unexpected crashes and burns.</li>\n<li>Add some band-aid where it hurts the most.</li>\n</ul>\n<p>In this article series, we will cover the basics of error handling and how it relates\nto proper abstraction levels for building robust software.</p>\n<h2 id=\"a-short-typology-of-errors\">A short typology of errors</h2>\n<p>An error is a <b>normal and regular event</b> during the life of most programs.\nIt happens whenever an assumption it made turns out to be wrong.\nFor instance, it tried to open a file, but the file does not exist, or it sends data\nto a remote server, but the laptop has lost Wifi signal.</p>\n<p>We can split errors on whether the condition that is not met is expected\nto happen.</p>\n<h3 id=\"unexpected-errors\">Unexpected errors</h3>\n<p><em>Ooops, did not think it could happen!</em></p>\n<p>Whenever an assumption made by a piece of code turns out to be wrong\nand the developer did not anticipate it could, it is an <em>unexpected error</em>.\nThe program can react in one of two ways:</p>\n<h4 id=\"undetected-unexpected-error\">Undetected, unexpected error</h4>\n<p>The progam is oblivious and keeps going. This is the most problematic case.</p>\n<div class=\"figure-wrapper\"><figure id=\"fig-1\"><img src=\"/files/gunshow-46970d17bbd378c94e8f9da96b1a5258.png\" alt=\"This is fine\" srcset=\"\"><figcaption><div class=\"figcaption-wrapper\">From K.C. Green's <a href=\"http://gunshowcomic.com/648\" class=\"external\" rel=\"external noopener noreferrer\">gunshot webcomic</a>.</div></figcaption></figure></div>\n<p>If an assumption turns out to be wrong, it means the program is in <strong>a state\nit was not intended</strong> to be in, ever. Therefore, the basis upon which its logic\nis built is no longer correct. Which means its result is very likely to be\nincorrect, too.</p>\n<p class=\"center\"><em>With unexpected state come unexpected consequences.</em></p>\n<p>As we do not want unexpected consequences, we must detect unexpected state as soon\nas it happens. To that effect, we must make our expectations <strong>explicit</strong>,\nso that the failure is detected.</p>\n<h4 id=\"detected-unexpected-error\">Detected, unexpected error</h4>\n<p>An assumption unexpectedly turns out to be wrong. Fortunately, the assumption was made\nexplicit, so the program crashes immediately.</p>\n<div class=\"figure-wrapper\"><figure id=\"fig-2\"><div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">class</span> <span class=\"token class-name\">SavingsAccount</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">def</span> <span class=\"token function\">withdraw</span><span class=\"token punctuation\">(</span>self<span class=\"token punctuation\">,</span> amount<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">assert</span> self<span class=\"token punctuation\">.</span>balance <span class=\"token operator\">>=</span> <span class=\"token number\">0</span>\n        <span class=\"token comment\"># proceed with withdrawal</span></code></pre></div><figcaption><div class=\"figcaption-wrapper\">Assertions are a common way to detect unexpected errors, such as invariant violations.</div></figcaption></figure></div>\n<p>In this example, we have a savings account. Its balance cannot, by definition, be negative.\nIn more formal wording, this is an <strong>invariant</strong> of class <code class=\"language-text\">SavingsAccount</code>.\nBy making this assumption explicit, we will trigger an immediate crash should <code class=\"language-text\">self.balance</code>\nbe negative.</p>\n<p><em>“But data will be lost.”</em></p>\n<p>If we reach a point where we have in our system a savings account with negative balance,\nsomething has already gone horribly wrong and our data is already corrupted.\nWe <strong>want</strong> our program to crash and we want it to crash <strong>now</strong>:</p>\n<ul>\n<li>It prevents <strong>contagion</strong>. Once the program's assumptions are incorrect, its state is no\nlonger under control, so its behavior is no longer under control. It might corrupt more data.\nIt might overwrite good data with invalid state. It might send invalid data to other\ncomponents.</li>\n<li>It ensures the failure is <strong>noticed</strong>. An exception can be silenced. An error can be\nignored. Who knows how long it will take until someone notices?</li>\n<li>It helps <strong>fixing</strong> the code. The longer the program runs after the initial divergence,\nthe harder time we will have locating the faulty code.</li>\n</ul>\n<p class=\"center\"><b>It is better to crash quickly and cleanly than to loose more data.</b></p>\n<p>The implication of assertions is that such an error cannot be recovered from, it is a defect\nin the program, that must be fixed by a person.</p>\n<h3 id=\"expected-errors\">Expected errors</h3>\n<p>When we anticipate the possibility than one of our assumptions might be wrong in\nan otherwise correct program, and we know a way to deal with it properly,\nwe have an <em>expected error</em>.</p>\n<p>Expected errors normally happen at the boundaries of interfaces, where a component\ntalks to another component. This could be, another part of the program, the operating\nsystem, a remote server or some hardware component.</p>\n<div class=\"figure-wrapper\"><figure id=\"fig-3\"><div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">def</span> <span class=\"token function\">load_settings</span><span class=\"token punctuation\">(</span>path<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">with</span> <span class=\"token builtin\">open</span><span class=\"token punctuation\">(</span>path<span class=\"token punctuation\">)</span> <span class=\"token keyword\">as</span> fd<span class=\"token punctuation\">:</span>\n        settings <span class=\"token operator\">=</span> json<span class=\"token punctuation\">.</span>load<span class=\"token punctuation\">(</span>fd<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> settings</code></pre></div><figcaption><div class=\"figcaption-wrapper\">This assumes way too much.</div></figcaption></figure></div>\n<p>This example makes many assumptions:</p>\n<ul>\n<li><code class=\"language-text\">path</code> contains a valid file path.</li>\n<li>that file path actually names an existing file.</li>\n<li>the program is allowed to open the file.</li>\n<li>the file contains a valid JSON document.</li>\n<li>no resource is exhausted in the process of reading and parsing the file.</li>\n</ul>\n<p>We notice those assumptions depend on external concepts and entities. Which means\nwe should reasonably expect them to fail in perfectly valid circumstances,\nand this should be handled gracefully.\nDepending on the context and specific error, this can mean:</p>\n<dl><dt>Recovering</dt><dd><p>Our code runs an alternate logic path to reach requested behavior. For instance, here,\nif opening the file fails, it could fall back to default settings.</p><div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">def</span> <span class=\"token function\">load_settings</span><span class=\"token punctuation\">(</span>path<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">try</span><span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">with</span> <span class=\"token builtin\">open</span><span class=\"token punctuation\">(</span>path<span class=\"token punctuation\">)</span> <span class=\"token keyword\">as</span> fd<span class=\"token punctuation\">:</span>\n            <span class=\"token keyword\">return</span> json<span class=\"token punctuation\">.</span>load<span class=\"token punctuation\">(</span>fd<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">except</span> <span class=\"token punctuation\">(</span>OSError<span class=\"token punctuation\">,</span> JSONDecodeError<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span> <span class=\"token comment\"># we target specific errors</span>\n        <span class=\"token keyword\">return</span> DEFAULT_SETTINGS</code></pre></div></dd><dt>Propagating the error</dt><dd><p>If our code is unable to handle the error condition at this level, it can propagate it to\na higher level, which will then choose how to handle it best. Possibly, such an error can\npropagate all the way to program entry point and terminate the program.</p><p><em>This is default behavior of python exceptions, so our code in <a href=\"#fig-3\" class=\"internal\">Fig. 3</a> does this.</em></p></dd><dt>Converting the error</dt><dd><p>The error as it arises in our code might not be meaningful outside of it. It might pertain\nto an implementation detail that should not be exposed.</p><p>For instance, our <code class=\"language-text\">load_settings</code> function is meant to encapsulate the details of settings\nloading. We use <code class=\"language-text\">json.load</code>, which will generate a <code class=\"language-text\">JSONDecodeError</code> if the document is\ninvalid. If we propagated this error to callers, we would expose internal details\nto them, breaking the encapsulation. To avoid this, our <code class=\"language-text\">load_settings</code> function should\nconvert the error into another error, using higher-level semantics.</p><div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">def</span> <span class=\"token function\">load_settings</span><span class=\"token punctuation\">(</span>path<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">try</span><span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">with</span> <span class=\"token builtin\">open</span><span class=\"token punctuation\">(</span>path<span class=\"token punctuation\">)</span> <span class=\"token keyword\">as</span> fd<span class=\"token punctuation\">:</span>\n            <span class=\"token keyword\">return</span> json<span class=\"token punctuation\">.</span>load<span class=\"token punctuation\">(</span>fd<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">except</span> JSONDecodeError <span class=\"token keyword\">as</span> err<span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">raise</span> ConfigurationError<span class=\"token punctuation\">(</span><span class=\"token string\">'User mistake in settings'</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">from</span> err</code></pre></div></dd></dl>\n<h3 id=\"errors-summary\">Errors Summary</h3>\n<p>So you are standing in front of a piece of code, evaluating it line after line, statement\nafter statement. For each and every expectations that it makes, the following diagram\nshould help picking the appropriate action:</p>\n<div class=\"figure-wrapper\"><figure id=\"fig-4\"><img src=\"/files/error-handling-diagram-8490de609b88dcf38dd8035bb2f7f65e.png\" alt=\"Error handling decision tree\" srcset=\"/files/error-handling-diagram-1.5x-7ad43db942725af4e6914871a6beaf67.png 1.5x, /files/error-handling-diagram-2x-0c6748c15f9cb9c64eb46e1f9bb2a7f4.png 2x\"><figcaption><div class=\"figcaption-wrapper\">Error handling decision tree</div></figcaption></figure></div>\n<p>This whole decision happens within one single function, on every expectation it makes. One\nkey part is the final step, at the very bottom: as soon as we propagate an error, we\nmust ensure it is properly handled one level higher up the chain of callers. And then at the\nnext level, and the next level, until either it is recovered from or it reaches the top of the\nchain, at which point the supporting environment will usually invoke its default handlers,\ntypically printing an error and terminating the program.</p>\n<p>We take note, however, that though we outlined the decision process, we have not answered\ntwo crucial questions:</p>\n<ul>\n<li>What exactly makes an error recoverable?</li>\n<li>What is meant by exposing internals?</li>\n</ul>\n<p>The key to answering those is keeping levels of abstraction consistent. That will be the\nfocus of the <a href=\"error-handling-levels\" class=\"internal\">next post</a> in this error handling series.</p>","fields":{"isPage":false,"slug":"/2019/error-handling-introduction"},"frontmatter":{"title":"Error Handling part 1: Introduction","classname":null,"date":"2019-02-19T00:00:00.000Z","formattedDate":"February 19, 2019","isoDate":"2019-02-19T00:00:00+00:00"},"headings":[{"value":"A short typology of errors","depth":1},{"value":"Unexpected errors","depth":2},{"value":"Undetected, unexpected error","depth":3},{"value":"Detected, unexpected error","depth":3},{"value":"Expected errors","depth":2},{"value":"Errors Summary","depth":2}],"image":{"original":{"src":"/static/gunshow-46970d17bbd378c94e8f9da96b1a5258.png"}},"series":{"name":"software","fullName":"software engineering","fields":{"slug":"/software"}},"tags":[{"name":"code","slug":"/tag/code"}]}},"pageContext":{"series":"software","slug":"/2019/error-handling-introduction","previous":{"fields":{"slug":"/2018/testing-aiohttp-client-part-2"},"frontmatter":{"title":"Unit Testing aiohttp Clients - part 2","series":"python"},"tags":[{"name":"code","slug":"/tag/code"},{"name":"testing","slug":"/tag/testing"}]},"next":{"fields":{"slug":"/2019/error-handling-levels"},"frontmatter":{"title":"Error Handling part 2: Abstractions","series":"software"},"tags":[{"name":"code","slug":"/tag/code"}]},"seriesPrevious":null,"seriesNext":{"fields":{"slug":"/2019/error-handling-levels"},"frontmatter":{"title":"Error Handling part 2: Abstractions","series":"software"},"tags":[{"name":"code","slug":"/tag/code"}]}}},
    "staticQueryHashes": ["1733002695","4006707078"]}