<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Oleg Sinavski</title>
    <description>The latest articles on Forem by Oleg Sinavski (@olegsinavski).</description>
    <link>https://forem.com/olegsinavski</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F817866%2F5e8e862d-ebf8-400b-988f-b7a0496c516b.png</url>
      <title>Forem: Oleg Sinavski</title>
      <link>https://forem.com/olegsinavski</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/olegsinavski"/>
    <language>en</language>
    <item>
      <title>Software for exploration</title>
      <dc:creator>Oleg Sinavski</dc:creator>
      <pubDate>Wed, 20 Mar 2024 16:49:07 +0000</pubDate>
      <link>https://forem.com/olegsinavski/software-for-exploration-pd</link>
      <guid>https://forem.com/olegsinavski/software-for-exploration-pd</guid>
      <description>&lt;p&gt;I worked in research all my life, so I know a stereotype that researchers write ugly code (e.g. see &lt;a href="https://www.the-scientist.com/how-to-fix-science-s-code-problem-70447"&gt;here&lt;/a&gt;, &lt;a href="https://techblog.bozho.net/the-astonishingly-low-quality-of-scientific-code/"&gt;here&lt;/a&gt;, or &lt;a href="https://www.quora.com/Why-do-many-people-from-academia-write-bad-code#:~:text=It's%20human%20nature%3A%20short%2Dterm,feel%20enough%20pressure%20to%20learn"&gt;here&lt;/a&gt;). But I thought: we can fix it, right? So multiple times I tried to design nice research frameworks. I tried to bring in interfaces and create nice abstractions using software engineering books and blogs I liked reading. &lt;/p&gt;

&lt;p&gt;But over and over again all those efforts went in vain. The majority of research software I worked on never went to production (although some did). It would have been great for my mental health if someone told me a simple truth: &lt;em&gt;dying research code is actually what is supposed to happen&lt;/em&gt;. Researchers should not spend much time engineering it in the first place.&lt;/p&gt;

&lt;p&gt;Professional software engineers always look down on researchers who are not using the best software practices. There are several posts trying to raise the bar of research code (e.g. this &lt;a href="https://invenia.github.io/blog/2020/07/07/software-engineering/"&gt;great post&lt;/a&gt; and a &lt;a href="https://goodresearch.dev/"&gt;research code handbook&lt;/a&gt;). But this post goes other way around: it argues how to not overdo the best software practices and instead invest only in fast exploration. It is targeted for research-oriented companies where your goal is to try out many ideas fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Take on some strategic tech debt
&lt;/h2&gt;

&lt;p&gt;A successful research project at a company has two phases: exploration and exploitation. In “exploration” you want to try out as many diverse solutions as you can. During “exploitation” you have to robustify the best solution and turn it into a useful product. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fih79u18b6huss7pkuyuw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fih79u18b6huss7pkuyuw.png" alt="During exploration many projects die out. You should build a robust solution only during exploitation." width="800" height="524"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Optimal software practices are quite different between the two. That's why companies often have separate research and product divisions. All the books you might typically read on software design are mainly about the second "exploitation" phase. In this phase you are building foundations for a scalable product. This is where all the design patterns come in: nice APIs, logging, error handling and so on.  &lt;/p&gt;

&lt;p&gt;But in the first "exploration" phase you are &lt;strong&gt;not&lt;/strong&gt; building foundations that will live forever. In fact, if the majority of your efforts survive, then you (by definition) did not explore enough.&lt;/p&gt;

&lt;p&gt;Many practices in this post are examples of what would normally become “tech debt”. It’s what you get by not writing clean reusable well-abstracted code. Is debt always bad? We prefer never getting a loan or a mortgage, but borrowing money is often a good strategy in life. It's ok to get into debt to move fast and profit later. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp4fjswqkc4z8fk4ivd8t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp4fjswqkc4z8fk4ivd8t.png" alt="It’s ok to get into software debt in research — you don’t have to repay all of it, only for the minority on the successful research paths." width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Similarly, by not taking technical debt you might be slowing down your research. The good news is that the majority of the time you don’t have to pay it back. Most of your research code is likely to die anyway. So on average, you will not be suffering from the whole tech debt you've taken.&lt;/p&gt;

&lt;h3&gt;
  
  
  The case against code reuse
&lt;/h3&gt;

&lt;p&gt;Many software architectures and refactoring techniques are specifically oriented to improve code reusability. There are generic &lt;a href="https://dev.to/pluralsight/reusable-code-the-good-the-bad-and-the-ugly-3do6"&gt;downsides&lt;/a&gt; to code reuse. But in production they are outweighed by the well-known benefits (for example, see &lt;a href="https://blog.filestack.com/the-benefits-of-reusing-code-for-software-development/"&gt;this typical post&lt;/a&gt;). In research projects, the majority of the code is destined to sink to oblivion. Striving for code reuse could actually slow you down.&lt;/p&gt;

&lt;p&gt;Limiting code reuse is the type of technical debt that is ok to take in research. There are several patterns of code reuse I want to discuss: adding an unneeded dependency, copypasting code, maintaining a lot of shared research code, premature design investments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Think twice before importing something new
&lt;/h3&gt;

&lt;p&gt;If you know a well-maintained versioned library that is going to speed you up - go for it! But before taking in a new dependency, try to make a judgment call whether it’s worth it. Every additional one brings you closer to dependency hell. It makes you invest time into learning and troubleshooting it. See more pitfalls of dependencies in this &lt;a href="https://builtin.com/software-engineering-perspectives/software-dependency-costs"&gt;concise post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It’s probably fine to depend on something if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you used it already, there is not much to learn, it has a large community, good docs and tests&lt;/li&gt;
&lt;li&gt;it is versioned, easy to install&lt;/li&gt;
&lt;li&gt;and finally, there is no way you can implement it yourself.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But be wary about a dependency if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you can’t figure out how to use it quickly, it is very new (or very old) or no one seems to know about it; there are no docs or tests&lt;/li&gt;
&lt;li&gt;it is from your monorepo and is constantly being changed by other teams&lt;/li&gt;
&lt;li&gt;it pulls in many other dependencies and tools; or it's just hard to install
&lt;/li&gt;
&lt;li&gt;and finally, you feel that you (or some LLM) can write this code in a few hours.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of an explicit dependency, you can follow a nice Go proverb: “&lt;a href="https://www.efekarakus.com/2021/09/23/a-little-copying-is-better-than-a-little-dependency.html"&gt;a little copying is better than a little dependency&lt;/a&gt;", which is our next topic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Copypaste gives you freedom of experimentation
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fguwd42hjv8vjyx206gu2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fguwd42hjv8vjyx206gu2.png" alt="Copypasting is fast and it sometimes the best tool during research." width="800" height="329"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some say that “&lt;a href="https://thecodist.com/copy_and_pasting_code_should_be_illegal/"&gt;copy-paste should be illegal&lt;/a&gt;”. But to my surprise I found myself arguing in favor of it quite often. Copypaste could be the optimal choice during the exploration phase.&lt;/p&gt;

&lt;p&gt;If you depend on a heavily-used function from another part of the codebase you can forget about easily changing it. You’re likely to break something for someone and have to spend precious time in code reviews and fixes. But if you copypaste the necessary code into your folder, you are free to do anything you want with it. This is a big deal in research projects where experimentation is a norm rather than an exception. Especially if you are not sure if changes are going to be useful for everyone.&lt;/p&gt;

&lt;p&gt;I find that deep learning codebases are suitable for copypasting the most. Typically, the amount of code needed to describe a model and its training is not so huge. But at the same it could be very nuanced and hard to generalize. Shareable training scripts tend to grow to an unmanageable size: e.g. Hugging Face &lt;code&gt;transformers&lt;/code&gt; &lt;a href="https://github.com/huggingface/transformers/blob/v4.38.2/src/transformers/trainer.py"&gt;Trainer&lt;/a&gt; has +4k lines. Interestingly enough, transformers opted for copypaste on the model level. Please check out their &lt;a href="https://huggingface.co/blog/transformers-design-philosophy"&gt;post&lt;/a&gt; with the reasoning behind their "single file model" policy. See more resources about the beauty of copypaste at the end.&lt;/p&gt;

&lt;p&gt;An alternative to copypaste is staying on a branch. But I feel like it brings too much overhead in teamwork. Also, I found several more posts about the beauty of copypaste - see more posts in the conclusion.&lt;/p&gt;

&lt;h3&gt;
  
  
  Maintaining shared research code is hard
&lt;/h3&gt;

&lt;p&gt;Maintenance of heavily used shared code requires a lot of work. Take a look at the &lt;code&gt;torch.nn.Module&lt;/code&gt; number of file lines plotted against the &lt;code&gt;Pytorch&lt;/code&gt; version. You can see that even the most advanced research teams struggle to keep the complexity in check.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frx37llej7fa9sre5jow1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frx37llej7fa9sre5jow1.png" alt="torch.nn.Module file length depending on PyTorch version. It is not getting simpler." width="800" height="530"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Don’t underestimate time and resources needed to maintain a large shared research code. The more a research library is used the more complicated it becomes. It happens faster than for a typical library because every research direction has a slightly different usecase. Establish very strict rules of what could be contributed back. Otherwise, the shared code becomes fragile and overgrown with a slew of options, buggy optimizations and edgecases. Since the majority of research code dies out, all this extra complexity will never be used again. Dropping some of your shared code will free up some time to do actual research.&lt;/p&gt;

&lt;h3&gt;
  
  
  Design for exploration, not for code reuse
&lt;/h3&gt;

&lt;p&gt;It is somewhat true that you don’t want &lt;a href="https://blog.developerpurpose.com/dont-future-proof-your-code-aad04ef75584#:~:text=If%20you%20find%20yourself%20future,do%20is%20build%20something%20simple"&gt;to future-proof your code&lt;/a&gt; too much even in production. Try to implement the simplest possible solution that meets the requirements. But in production code there are always aspects of maintainability to consider. For example, error handling, speed, logging, modularization is what you typically need to think about.&lt;/p&gt;

&lt;p&gt;In research code, none of that matters. You just want to quickly prove that an idea is good or bad in the fastest possible manner and move on. So the dirty simplicity that achieves it without any modules or APIs is totally ok!&lt;/p&gt;

&lt;p&gt;Don’t waste valuable time on premature software investments such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;creating component interfaces too early in the project. You'll spend too much time fitting into self-made artificial constraints&lt;/li&gt;
&lt;li&gt;optimizing deep learning training infrastructure before committing to deep learning solution&lt;/li&gt;
&lt;li&gt;using production config/factories/serialization systems or base classes. Quite often you don't need their functionality during prototyping&lt;/li&gt;
&lt;li&gt;overly-strict linting and type checking systems. No reason to slow down fast-changing throw-away research code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Invest into fast exploration
&lt;/h2&gt;

&lt;p&gt;The goal of a research project is to find a novel solution. No one knows (by definition) what it looks like. It is akin to an optimization process in a complicated research landscape with limited information. To find a good minimum, you need to try many paths, recognize good and bad paths and not get stuck in local minima. To do all of it fast, you sometimes need to make software investments instead of taking tech debt.&lt;/p&gt;

&lt;h3&gt;
  
  
  Speed up common paths
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3j9zfb15mxrl11jtexhk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3j9zfb15mxrl11jtexhk.png" alt="Invest into speed in up common parts of your research projects." width="800" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are several different research paths you want to try. Is there a design, a library or an optimization that would shave off time from the majority of paths? You should be careful to not over-engineer anything because you don’t always know all the ideas you’re about to try. This is very custom to every project, but here are some examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;if you train deep networks, invest into training infrastructure. Figure out hyperparameters allowing you to converge during training quickly and reliably &lt;/li&gt;
&lt;li&gt;if every experiment requires you to use a different model, figure out how you can quickly swap them (e.g. by using a simple factory system or just copypaste) &lt;/li&gt;
&lt;li&gt;if every experiment has too many parameters and is hard to manage, invest into a nice configuration library.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Branch out quickly
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4evf0p1itvz2wmwuke68.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4evf0p1itvz2wmwuke68.png" alt="Invest into velocity of initiating new research paths. You need many diverse directions to find the solution." width="768" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Researchers should be able to initiate new diverse ideas quickly. It seems easy at the start of the project. But then it gradually becomes harder and harder as people get entrenched in their favourite research paths. To address this, cultural and organizational changes are essential. There should be a process stopping non-promising research before sinking too much money and emotion into it. Regular demo days and technical peer reviews can serve as effective strategies for this purpose. It's also important to find a balance between people jumping on a new shiny idea and properly closing current projects. &lt;/p&gt;

&lt;p&gt;But this is a software post, so here are some practices to make branching out new projects easy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;keep evaluation code disentangled from algorithms. Evaluations are typically more stable than research directions&lt;/li&gt;
&lt;li&gt;embrace starting a new project from a blank slate, but then watch out which components are reused. Modularizing and cleaning them up is a good investment&lt;/li&gt;
&lt;li&gt;in a new research project, implement the most innovative and risky component first. Doing so identifies the majority of bottlenecks guiding future software design.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Increase signal to noise ratio
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0mu7cv28aamsxdagawlh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0mu7cv28aamsxdagawlh.png" alt="Bugs and non-determinism can derail research projects and make results inconclusive" width="800" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Noisy and buggy code makes results so ambiguous and inconclusive that the whole project is going to be a waste of time. While you shouldn't over engineer things, you can easily follow these simple rules of thumb to avoid messy code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;avoid code with &lt;a href="https://sinavski.com/post/3_side_effects/"&gt;side-effects&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;default to functions rather than classes; and with classes, prefer encapsulation vs. inheritance&lt;/li&gt;
&lt;li&gt;minimize length of functions/classes/modules; minimize number of if-statements &lt;/li&gt;
&lt;li&gt;know python well, but use simple techniques. Resist the temptation going into intellectual weeds of metaclasses, decorators and functional programming.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Software that produces different results during different runs is tricky to work with. If you made an important but wrong decision based on an unlucky seed, you are going to waste a lot of time recovering. Here are some tips when dealing with non-deterministic software:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;understand whether noisy is coming from the algorithm or it's evaluation. Noise sources compound and you should strive for completely deterministic evaluation. &lt;/li&gt;
&lt;li&gt;don’t stop finding sources of randomness until you really get a reproducible script. Remember that after finding all random seeds, noise might come from data or from generic functions with side-effects.&lt;/li&gt;
&lt;li&gt;vary seeds and determine baseline variance of your results. Don’t take decisions on non-statistically significant results.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The punchline comes from &lt;a href="https://qr.ae/psI7J4"&gt;this post&lt;/a&gt; about research code: &lt;em&gt;“You don't bother with [good software design] because the code is not the point. The code is a tool that gets you the answer that you need”&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;It is extremely important to have great coding foundations. But at the end of the day, the exploration and actually useful product is what matters. If you use too much production software in research, you waste time needed to discover something new. Instead, find what slows down your exploration process. Speed up research paths by investing in fast branching, time to results and clean noiseless code.&lt;/p&gt;

&lt;p&gt;It would be crazy to argue completely against code reuse. I just want to point out that code reuse should be a well-balanced activity. In research, the ratio of throw-away code is larger than in production. The balance is tilted further against reuse. Here are few more great posts with pitfalls of code reuse:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/pluralsight/reusable-code-the-good-the-bad-and-the-ugly-3do6"&gt;Reusable Code: The Good, The Bad, and The Ugly&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://thinkingsideways.net/code/code-duplication.html"&gt;When It Makes Sense to Repeat Yourself&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://softwareengineering.stackexchange.com/questions/448168/why-should-we-reuse-code-as-binary-modules-instead-of-copy-pasting/448178#448178?newreg=3302e0728d254fe38f5bf964d9c1c787"&gt;Balanced StackExchange with pros and cons of reuse&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And here are few more posts arguing for copypasting practices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dzone.com/articles/copy-and-paste-programming"&gt;Is copypaste really a problem?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://betterprogramming.pub/when-developers-are-allowed-to-copy-and-paste-the-code-d8db988c78ae"&gt;When Should You Copy-Paste Code?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://javascript.plainenglish.io/the-good-and-the-bad-of-copy-paste-programming-6483d7ecb340"&gt;The Good and the Bad of Copy-Paste Programming&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.reddit.com/r/datascience/comments/10lz6vf/how_often_do_you_copy_and_paste_code/"&gt;"As a senior with 5 YOE, I feel like part of my pay is knowing what code to copy lol"&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thank you for reading! I feel some bits are a bit controversial, please let me know in the comments! &lt;/p&gt;

</description>
      <category>softwaredevelopment</category>
      <category>softwareengineering</category>
      <category>architecture</category>
      <category>ai</category>
    </item>
    <item>
      <title>Killing Python side effects in pictures</title>
      <dc:creator>Oleg Sinavski</dc:creator>
      <pubDate>Wed, 09 Aug 2023 17:49:03 +0000</pubDate>
      <link>https://forem.com/olegsinavski/killing-python-side-effects-in-pictures-2h7k</link>
      <guid>https://forem.com/olegsinavski/killing-python-side-effects-in-pictures-2h7k</guid>
      <description>&lt;h2&gt;
  
  
  Minimize side effects
&lt;/h2&gt;

&lt;p&gt;Generating a random number, communicating over the network, or controlling a robot are all examples of side effects.&lt;/p&gt;

&lt;p&gt;If the software can’t affect the external world, it is pointless. But &lt;em&gt;unnecessary&lt;/em&gt; side effects can cause problems and better be avoided (&lt;a href="https://dev.to/olegsinavski/all-dangers-of-side-effects-for-python-coders-5g6d"&gt;see the previous post&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Advertisement for this post:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python-oriented, with examples&lt;/li&gt;
&lt;li&gt;No intention to push functional programming per see&lt;/li&gt;
&lt;li&gt;Develops visual thinking for pragmatic refactoring&lt;/li&gt;
&lt;li&gt;Many links to other great posts and discussions&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What are we striving for?
&lt;/h2&gt;

&lt;p&gt;A good (&lt;a href="https://betterprogramming.pub/what-is-a-pure-function-3b4af9352f6f" rel="noopener noreferrer"&gt;"pure"&lt;/a&gt;) function should&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;not cause any changes outside its scope (avoid exerting side effects on the system)&lt;/li&gt;
&lt;li&gt;produce the same output for the same set of inputs (avoid relying on side effects from the system)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;People &lt;a href="https://stackoverflow.com/questions/4391524/is-returning-a-random-value-from-function-a-side-effect" rel="noopener noreferrer"&gt;debate&lt;/a&gt; whether the second property should be called a "side effect". I found it important to identify and distinguish "input" vs "output" side effects, at least in Python. So in the &lt;a href="https://www.reddit.com/r/functionalprogramming/comments/15axgtp/a_concise_name_for_code_lines_without_side/" rel="noopener noreferrer"&gt;absence of a good terminology&lt;/a&gt;, I will call incoming side effects &lt;strong&gt;“side arguments”&lt;/strong&gt; and outgoing external side effects &lt;strong&gt;“side results.”&lt;/strong&gt; It seems like 65% of people are &lt;a href="https://en.wikipedia.org/wiki/Visual_thinking" rel="noopener noreferrer"&gt;visual thinkers&lt;/a&gt;, so here are some pictures for the majority:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dirty function&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fue1fu4z9mgki8qupghce.jpeg" alt="Function with side effect (scheme)"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1c2js4ut7dog3bc2abap.jpeg" alt="Function with side effect (example)"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Calling system &lt;code&gt;time.time()&lt;/code&gt; is an "input" side effect from the external system on the function. The &lt;code&gt;print&lt;/code&gt; statement is an "output" side effect from the function to the external world. If one removes all side effects from this red "dirty" function, we will get a side-effect-free green one:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Clean function&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq3jabrhzn0u7n4j36l1s.jpeg" alt="Function without side effect (scheme)"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4lws5tvltqx5enje1my1.jpeg" alt="Function without side effect (example)"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you don't bother cleaning up side effects, your software will look something like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dirty application&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flupdirz2vokjmlxnghme.jpeg" alt="Dirty application (scheme)"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F526r2j46o0jibufwck3d.jpeg" alt="Dirty application (example)"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There are many dirty red functions called by top functions (larger circles). Both have side effects (side-arguments and side-results). Occasionally, a nice clean green function is buried underneath. &lt;/p&gt;

&lt;p&gt;It’s important to understand what is the goal state here. We do not want to remove all side effects. Software that doesn’t interact with the world is truly useless. What we want to strive for is a picture like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Clean application&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F65hgb5sk6keu7a2uhaer.jpeg" alt="Clean application (scheme)"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5c8k6eq4mk4hvs8zupim.jpeg" alt="Clean application (example)"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All necessary side effects are pushed to the application's boundary. But all the internal core logic is green and clean! &lt;/p&gt;

&lt;p&gt;This is what's called &lt;a href="https://www.destroyallsoftware.com/screencasts/catalog/functional-core-imperative-shell" rel="noopener noreferrer"&gt;"Functional core, imperative shell"&lt;/a&gt;. There are at least a dozen posts I found about that idea. After digging around, it seems like &lt;a href="https://uvsmtid.info/shells-cores/" rel="noopener noreferrer"&gt;this one&lt;/a&gt; is quite clean and has nice pictures. But also see this &lt;a href="https://kennethlange.com/functional-core-imperative-shell/" rel="noopener noreferrer"&gt;summary post&lt;/a&gt; and a &lt;a href="https://news.ycombinator.com/item?id=18043058" rel="noopener noreferrer"&gt;discussion&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start from reusable utility functions
&lt;/h2&gt;

&lt;p&gt;Figuring out the correct order of refactoring can speed up the cleanup dramatically.&lt;/p&gt;

&lt;p&gt;Notice that a dirty function down the stack infects all functions that call it. If there are ten-layer-deep function calls and a small utility at the bottom is dirty, the whole ten-layer stack is spoiled.&lt;/p&gt;

&lt;p&gt;The first thing to do is to clean up frequently reused elementary functions and classes.&lt;/p&gt;

&lt;p&gt;Hence, here is a good refactoring order:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Identify highly reused dirty functions that are lowest in the call stack&lt;/li&gt;
&lt;li&gt;Clean them up (see below)&lt;/li&gt;
&lt;li&gt;Keep going until you run out of simple functions&lt;/li&gt;
&lt;li&gt;Move a level up the call stack &lt;/li&gt;
&lt;li&gt;Find reused dirty functions that are new lowest in the call stack&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;li&gt;Strive for the "Functional core, Imperative shell" ideal!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F81wboeqya4e7vyilfn0a.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F81wboeqya4e7vyilfn0a.gif" alt="Refactoring order"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Remove some side effects altogether
&lt;/h2&gt;

&lt;p&gt;Can you rewrite your function without affecting any external world whatsoever? That would be the best option! Sometimes neglected, but the first step is to remove unnecessary stuff. Here are some common examples. &lt;/p&gt;

&lt;p&gt;Often, people think their logging statements will be helpful for others forever:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;inverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A message that I feel everyone&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; 
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;would benefit from!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In reality, those logs are probably helpful just for you and only this week. A good rule of thumb is to remove most prints and logs from your branch before merging into the mainline. Most likely, you wouldn’t even notice the lack of a log message.&lt;/p&gt;

&lt;p&gt;Another typical case is initializing an external resource for no reason. Quite often in machine learning, I see algorithms that create folders for saving results&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;algorithm_1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;folder_with_results&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is not an algorithm’s job to create folders. Instead, it should be moved into the application initialization logic.&lt;/p&gt;

&lt;p&gt;Sometimes people forget to remove a previously useful side effect after refactoring or debugging.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;algorithm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argument&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;set_theano_flags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;  &lt;span class="c1"&gt;# Init something used 5 years ago
&lt;/span&gt;    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(...&lt;/span&gt; &lt;span class="n"&gt;complicated&lt;/span&gt; &lt;span class="n"&gt;logic&lt;/span&gt; &lt;span class="n"&gt;based&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;argument&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To summarize, a decent percentage of side effects could be just removed completely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simple refactoring flow
&lt;/h2&gt;

&lt;p&gt;What is the simplest refactoring recipe to make functions pure? Here is where the split on side arguments and results comes in handy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;move side-arguments up the function body. Then transform it into a regular argument.&lt;/li&gt;
&lt;li&gt;move side-results down the function body. Then return the side effect together with other results.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some visuals for you:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fewpqna42hwh9lueroc6w.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fewpqna42hwh9lueroc6w.gif" alt="Single function refactoring"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s look at the real-life example I encountered. The following function performs some random image augmentations. The researcher sometimes wants to debug intermediate random masks and dumps them into temp files.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffn18gbe8o5inxjxl2253.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffn18gbe8o5inxjxl2253.jpeg" alt="Single function refactoring - start"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This function has 2 side-arguments: it reads from the global random generator for the random mask and a cutout.&lt;br&gt;
It has 4 side-results:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;change global random generator state 2 times&lt;/li&gt;
&lt;li&gt;writes a temp debug file&lt;/li&gt;
&lt;li&gt;prints out debug file name&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This function is hard to test and will always bring you trouble by polluting &lt;code&gt;/tmp&lt;/code&gt; and the console.&lt;/p&gt;

&lt;p&gt;First, let's move side-arguments up and side-results down. This step could be skipped, but I found it very useful in more convoluted and long functions. We create a random generator once from a global state and move debug outputs down:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foeluearsclwimyd3dlia.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foeluearsclwimyd3dlia.jpeg" alt="Single function refactoring - almost done"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, let's merge "side" and "regular" arguments and results. We arrive at a clean image augmentation logic:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz8bqe7tqp8ramxccvtj1.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz8bqe7tqp8ramxccvtj1.jpeg" alt="Single function refactoring - finish"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are taking a random generator in and returning the debug mask. The high-level function is free to choose whether to pass seeded &lt;code&gt;rng&lt;/code&gt; for reproducibility or use the global one for convenience. It will also decide how to save the debug masks if still needed.&lt;/p&gt;
&lt;h2&gt;
  
  
  Short-circuit side effects
&lt;/h2&gt;

&lt;p&gt;Side-effects volume grows non-linearly. When one developer adds a side argument or a result, it encourages others to use a similar side channel.&lt;/p&gt;

&lt;p&gt;When a function reads globals (envvars, files), it compels other functions to write to globals (envvars, files) so as to influence its behavior.&lt;/p&gt;

&lt;p&gt;On the flip side, the more you remove, the easier it is to deal with the rest. After you move all side effects from the bottom up to upper functions, you typically find that you can completely "short-circuit" some. Here is a visual:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgtv253zbqua8h1rcik0g.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgtv253zbqua8h1rcik0g.gif" alt="Short-circuit side effects"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's look at a real but a bit longer example. Here is an often-encountered pattern in research code to use the filesystem for passing parameters around:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;create a default config file -&amp;gt;&lt;/li&gt;
&lt;li&gt;modify the config file -&amp;gt;&lt;/li&gt;
&lt;li&gt;read out from a config file&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Below is an extract from the actual code I encountered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;dump_default_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="n"&gt;default_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hidden_size&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;learning_coeff&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;wb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="n"&gt;pickle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default_config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;network_config_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;network_config_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pickle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;network&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;prediction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;prediction&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;network_main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="n"&gt;config_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;my_config.pkl&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
   &lt;span class="nf"&gt;dump_default_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="c1"&gt;# update the learning coefficient in the config file
&lt;/span&gt;   &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pickle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;learning_coeff&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1e-4&lt;/span&gt;  &lt;span class="c1"&gt;# better learning coefficient
&lt;/span&gt;   &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;wb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="n"&gt;pickle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;run_network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we discussed, let's move side arguments and results up the stack. First, move up the saving of the default config, and second, move up the config loading.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_default_config&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hidden_size&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;learning_coeff&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="n"&gt;network&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;prediction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;prediction&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;network_main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="n"&gt;config_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;my_config.pkl&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
   &lt;span class="n"&gt;default_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_default_config&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;wb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="n"&gt;pickle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default_config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

   &lt;span class="c1"&gt;# update learning coefficient in the config file
&lt;/span&gt;   &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pickle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;learning_coeff&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1e-4&lt;/span&gt;  &lt;span class="c1"&gt;# better learning coefficient
&lt;/span&gt;   &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;wb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="n"&gt;pickle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pickle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;run_network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can short-circuit all the file system calls and arrive at neat, side-effect-free code. On top of being considerably smaller and simpler, it is also much faster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_default_config&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hidden_size&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;learning_coeff&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="n"&gt;network&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;prediction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;prediction&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;network_main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_default_config&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;learning_coeff&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1e-4&lt;/span&gt;  &lt;span class="c1"&gt;# better learning coefficient
&lt;/span&gt;   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;run_network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To short-circuit a side effect, you should first identify input and output side effects of the same type (files, globals, envvars). Then you can lift all of them up the stack and remove them together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Expose side effects to developers
&lt;/h2&gt;

&lt;p&gt;Side effects might bring you problems, but &lt;strong&gt;hidden&lt;/strong&gt; side effects are the worst. Imagine you decided to use an external library to make a friendly math-related application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;external_library&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;compute_optimal_solution&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
   &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Enter the number&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;compute_optimal_solution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Optimal value is :&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You happily deploy it only to receive user complaints about database-related crashes. You're really surprised since you just wanted to provide some math utility and never intended to deal with databases. Looking into the source of &lt;code&gt;compute_optimal_solution&lt;/code&gt;, you might find something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;compute_optimal_solution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
   &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
       &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

   &lt;span class="c1"&gt;# to understand how people use our function, 
&lt;/span&gt;   &lt;span class="c1"&gt;# we log the results in the debug database
&lt;/span&gt;   &lt;span class="n"&gt;database_cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DEFAULT_DB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;database_cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INSERT INTO MyDB (argument, solution) VALUES (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;database_cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You respect the developer's desire to collect debugging data, but you would never have guessed it upfront. It would have saved you so much time if this function was named appropriately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;compute_optimal_solution_and_cache_solution_in_database(x)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'd quickly realize that this function isn't suitable for your basic math script.&lt;/p&gt;

&lt;p&gt;It is a generic rule that the name of the function should describe what this function does. The same goes for side effects - if your function has a side effect, you better put it in its name. Using this naming rule, you’ll find that the ugliest and the most dangerous functions have the longest names!&lt;/p&gt;

&lt;p&gt;You also can expose side effects by splitting “clean” and “dirty” code on the module level. For example, a library-like folder should have only clean side-effect-free code. All side effects should go into an application-like folder (e.g., &lt;code&gt;scripts&lt;/code&gt;, &lt;code&gt;app&lt;/code&gt;, or &lt;code&gt;runners&lt;/code&gt;). Here is another &lt;a href="https://medium.com/nerd-for-tech/what-are-side-effects-in-programming-51f7ef340f98" rel="noopener noreferrer"&gt;nice short post&lt;/a&gt; reinforcing this point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Misc and advanced
&lt;/h2&gt;

&lt;p&gt;The points above should work for the majority of side-effect cleanups. But there are many exceptions and advanced concepts that wouldn't fit this post. Here are some follow-ups.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Dependency injection?&lt;/em&gt;&lt;br&gt;
Injecting an object that &lt;em&gt;might&lt;/em&gt; produce a side effect instead of producing it yourself is a common way to kick a can down the road:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pass random generator instead of querying it&lt;/li&gt;
&lt;li&gt;pass &lt;code&gt;Timer&lt;/code&gt; instead of &lt;code&gt;time.time()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;pass &lt;code&gt;logging.Logger&lt;/code&gt; instead of a &lt;code&gt;print&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After reading many resources, &lt;a href="https://stackoverflow.com/questions/130794/what-is-dependency-injection" rel="noopener noreferrer"&gt;this thread&lt;/a&gt; probably stays the best.&lt;br&gt;
Here is a great &lt;a href="https://jrsinclair.com/articles/2018/how-to-deal-with-dirty-side-effects-in-your-pure-functional-javascript/" rel="noopener noreferrer"&gt;JavaScript post&lt;/a&gt; about it (and side effects). In a liberal interpretation, replacing a side-argument with a regular one &lt;a href="https://www.jamesshore.com/v2/blog/2006/dependency-injection-demystified" rel="noopener noreferrer"&gt;could be called&lt;/a&gt; a dependency injection. One should be careful not to overdo it (&lt;a href="https://stackoverflow.com/questions/2407540/what-are-the-downsides-to-using-dependency-injection" rel="noopener noreferrer"&gt;1&lt;/a&gt;, &lt;br&gt;
&lt;a href="https://softwareengineering.stackexchange.com/questions/371722/criticism-and-disadvantages-of-dependency-injection" rel="noopener noreferrer"&gt;2&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Return a functor?&lt;/em&gt;&lt;br&gt;
Instead of causing a side effect right away, you can return a "lazy" function that would do it later. See &lt;a href="https://dev.to/ruizb/side-effects-21fc"&gt;the post&lt;/a&gt; about this technique. Also, see the "Lazy functions" section in &lt;a href="https://jrsinclair.com/articles/2018/how-to-deal-with-dirty-side-effects-in-your-pure-functional-javascript/" rel="noopener noreferrer"&gt;this post&lt;/a&gt;. It's a fun long read, but it probably goes beyond the needs of a regular Python mortal.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Copy an input container?&lt;/em&gt;&lt;br&gt;
Modifying an incoming &lt;code&gt;list&lt;/code&gt; or &lt;code&gt;dict&lt;/code&gt; is also a side effect. &lt;br&gt;
Quite often, it's worth copying, modifying and returning it instead. See the discussion &lt;a href="https://stackoverflow.com/questions/20569142/is-there-any-way-to-prevent-side-effects-in-python" rel="noopener noreferrer"&gt;here&lt;/a&gt; and &lt;a href="https://goodresearch.dev/decoupled.html#kernighan" rel="noopener noreferrer"&gt;this post&lt;/a&gt;. Is it slower? Probably, yes. But the actual question should be this:&lt;br&gt;
will your company spend more money on (A) executing slower code or (B) debugging bugs caused by side-effects? Without picking (A) or (B), there is no basis for fear of slow but robust code.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;prints and loggers?&lt;/em&gt;&lt;br&gt;
While &lt;code&gt;logging&lt;/code&gt; is a side effect, it's not the worst one.  At least, the majority of developers don't treat it as such.&lt;br&gt;
It's hard to advise anything specific without going on a long tangent. You can adopt configurable &lt;code&gt;logging&lt;/code&gt;, pass a logger as a dependency to every function, return string messages or stick with &lt;code&gt;print&lt;/code&gt; (e.g. &lt;a href="https://pythonspeed.com/articles/python-multiprocessing/" rel="noopener noreferrer"&gt;if you use multiprocessing&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion:
&lt;/h2&gt;

&lt;p&gt;At the end of the day, remember to be pragmatic. You don't want to remove all side effects, you just want to remove unnecessary ones. In practice though, many side effects could be avoided. So you better allocate some time for refactoring and deal with them using some recipes from this post:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;make a pass on your code and remove some side effects altogether&lt;/li&gt;
&lt;li&gt;identify side-arguments and side-results and merge them with regular arguments or results &lt;/li&gt;
&lt;li&gt;identify reused utilities and clean them up first&lt;/li&gt;
&lt;li&gt;notice that some side-effects are caused by each other and short-circuit them&lt;/li&gt;
&lt;li&gt;expose side effects to developers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thank you for reading!&lt;/p&gt;

</description>
      <category>python</category>
      <category>cleancode</category>
      <category>refactoring</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>All Dangers of Side Effects for Python Coders</title>
      <dc:creator>Oleg Sinavski</dc:creator>
      <pubDate>Tue, 04 Jul 2023 11:30:44 +0000</pubDate>
      <link>https://forem.com/olegsinavski/all-dangers-of-side-effects-for-python-coders-5g6d</link>
      <guid>https://forem.com/olegsinavski/all-dangers-of-side-effects-for-python-coders-5g6d</guid>
      <description>&lt;p&gt;Are there enough posts about unnecessary side effects? Unlikely - you still see them everywhere.&lt;br&gt;
Here is a sales pitch for this post:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;no functional programming jargon&lt;/li&gt;
&lt;li&gt;a go-to list of reasons to avoid side effects&lt;/li&gt;
&lt;li&gt;Python-oriented&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Spotting side effects
&lt;/h2&gt;

&lt;p&gt;Some posts describe side effects in the context of functional programming (and JavaScript): &lt;a href="https://jessewarden.com/books/real-world-functional-programming/part1/02_what_is_a_side_effect.html"&gt;one&lt;/a&gt;, &lt;a href="https://www.yld.io/blog/the-not-so-scary-guide-to-functional-programming/"&gt;two&lt;/a&gt;, &lt;a href="https://thejs.dev/jmitchell/what-are-side-effects-and-what-you-can-do-about-them-jws"&gt;three&lt;/a&gt; and a &lt;a href="https://en.wikipedia.org/wiki/Side_effect_(computer_science)"&gt;wiki&lt;/a&gt;. You don't have to study functional programming to grasp this concept. Unfortunately, unnecessary side effects are prevalent in all coding paradigms, especially OOP.&lt;/p&gt;

&lt;p&gt;Let’s take a simple function that computes the absolute value of a number:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What comes out from the &lt;code&gt;return&lt;/code&gt; statement is called a “primary effect”. Now let’s take a look at another function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;abs_effectful&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;

   &lt;span class="n"&gt;twitter_api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TwitterAPI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;some_twitter_id&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
   &lt;span class="n"&gt;chat_bot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LanguageModel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="n"&gt;tweet_content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;twitter_api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read_first_tweet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chat_bot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tweet_content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; is important because "&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;twitter_api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;twitter_api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we also have the same primary effect returned by the abs_effectful function. But a section in the middle performs pretty surprising actions and influences the external world: it has side effects.&lt;/p&gt;

&lt;p&gt;You may ask: "&lt;em&gt;Why do you have a weird unrelated piece of code in your function??&lt;/em&gt;" This contrived example is not so far from what happens in large codebases. It’s just a set of refactorings, misunderstandings, and a stream of people joining and leaving teams. Time passes, and simple code accumulates &lt;strong&gt;unnecessary&lt;/strong&gt; baggage.&lt;/p&gt;

&lt;p&gt;Here are some real examples of side effects that I've encountered in my Python ML career: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;publishing a number to the Weights-and-Biases server out of the loss function&lt;/li&gt;
&lt;li&gt;dumping debug images into a home folder from a computer vision algorithm&lt;/li&gt;
&lt;li&gt;changing an environmental variable inside a function&lt;/li&gt;
&lt;li&gt;dropping to &lt;code&gt;pdb&lt;/code&gt; on &lt;code&gt;if&lt;/code&gt; condition&lt;/li&gt;
&lt;li&gt;changing a global variable&lt;/li&gt;
&lt;li&gt;calling &lt;code&gt;sys.exit&lt;/code&gt; inside an algorithm&lt;/li&gt;
&lt;li&gt;sending a Slack message if &lt;code&gt;nan&lt;/code&gt; is encountered during training&lt;/li&gt;
&lt;li&gt;... and so on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After seeing many of those, you get a &lt;a href="https://marvelanimated.fandom.com/wiki/Spider-Sense"&gt;Spider-Sense&lt;/a&gt; for spotting side effects. If you get surprised by what function does, quite often the function has a side effect. In fact, the code with unnecessary side effects &lt;strong&gt;is not as simple as it could be&lt;/strong&gt;. See &lt;a href="https://medium.com/javascript-scene/the-single-biggest-mistake-programmers-make-every-day-62366b432308"&gt;a great post from Eric Elliot&lt;/a&gt; about simplicity, surprises and side effects.&lt;/p&gt;

&lt;p&gt;Side effects are unavoidable because software must have some influence on the world. But are unnecessary side effects harmful? If there are no visible differences, is there a reason to prefer one script over another?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NvRr8pH6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j1m1icazhuqx1kv9e5g9.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NvRr8pH6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j1m1icazhuqx1kv9e5g9.jpeg" alt="More than 7 plagues of side effects (Credit: John Martin, public domain)" width="800" height="537"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Unexpected crashes
&lt;/h2&gt;

&lt;p&gt;How can &lt;code&gt;abs&lt;/code&gt; crash? It pretty much can’t. What about &lt;code&gt;abs_effectful&lt;/code&gt;? It might crash in almost every line:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a Twitter API might fail to initialize&lt;/li&gt;
&lt;li&gt;a language model can't allocate GPU memory&lt;/li&gt;
&lt;li&gt;it might fail to parse or generate a phrase&lt;/li&gt;
&lt;li&gt;a Twitter API might fail to publish a response or crash upon closing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every side-effect code line might crash since it deals with things you don't control. Now, from the real examples above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Weights-and-Biases or Slack might be unavailable &lt;/li&gt;
&lt;li&gt;you might run out of space in the home folder&lt;/li&gt;
&lt;li&gt;an environmental variable is deleted before setting, and so on ...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the code with side effects is more fragile.&lt;/p&gt;

&lt;h2&gt;
  
  
  Unexpectedly slow (and occasionally &lt;em&gt;very slow&lt;/em&gt;) code
&lt;/h2&gt;

&lt;p&gt;Every side effect might bring computational costs. But most importantly, it brings timing variability into your software. Because side-effect code typically deals with uncontrolled external resources, you get uncontrolled slowdowns. Every side-effect code line in &lt;code&gt;abs_effectful&lt;/code&gt; can bring you a massive slowdown:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a Twitter API could be slow to connect&lt;/li&gt;
&lt;li&gt;a language model might wait for free GPU memory&lt;/li&gt;
&lt;li&gt;computational load on the system might make phrase parsing too slow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In real-world code, similar things can happen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;database access could be very slow because the network is busy&lt;/li&gt;
&lt;li&gt;writing to a hard drive could be slow because of fragmentation&lt;/li&gt;
&lt;li&gt;logging could be too slow because other loggers compete for file access, and so on ...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The bottom line is that the code with side effects is slower.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hard to use it concurrently
&lt;/h2&gt;

&lt;p&gt;You must be careful about any state you modify in concurrent (or even worse &lt;a href="https://stackoverflow.com/questions/1897993/what-is-the-difference-between-concurrent-programming-and-parallel-programming"&gt;parallel&lt;/a&gt;) software to avoid &lt;a href="https://verdagon.dev/blog/python-data-races"&gt;data races&lt;/a&gt; and &lt;a href="https://superfastpython.com/thread-deadlock-in-python/"&gt;deadlocks&lt;/a&gt;. Code with side effects modifies the external state. It will often require a significant redesign to be used in a concurrent context. You need to ensure that only one function will use a global resource simultaneously and that the order of operations doesn't break the logic.&lt;/p&gt;

&lt;p&gt;The timing variability mentioned above makes it even worse.&lt;br&gt;
It's a big topic, so here is a &lt;a href="https://thejs.dev/jmitchell/what-are-side-effects-and-what-you-can-do-about-them-jws"&gt;good post&lt;/a&gt; discussing concurrency and shared state access in depth.&lt;/p&gt;
&lt;h2&gt;
  
  
  Hard to optimize
&lt;/h2&gt;

&lt;p&gt;Optimizing a function that doesn't modify anything externally is more straightforward. You can reorder operations inside it and cache the computation results. Since you're not constrained by an external call for a side effect, you can choose any tool for the job. Many side-effect-free functions can be easily rewritten in &lt;code&gt;numpy&lt;/code&gt;/&lt;code&gt;torch&lt;/code&gt;/&lt;code&gt;jax&lt;/code&gt; and &lt;code&gt;Pybind&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So on top of being slow, code with side effects is hard to speed up.&lt;/p&gt;
&lt;h2&gt;
  
  
  Hard to understand
&lt;/h2&gt;

&lt;p&gt;What if I ask you to make a docstring for &lt;code&gt;abs_effectful&lt;/code&gt;? You will definitely struggle to write a concise story about the purpose of that function. &lt;/p&gt;

&lt;p&gt;A function with a single return statement like &lt;code&gt;abs&lt;/code&gt; achieves a single result. At a minimum, you can concisely write a docstring as&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="s"&gt;'''This function computes an absolute value.'''&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If there are side effects, you have to add them all one by one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="s"&gt;'''This function computes an absolute value 
AND opens Twitter API and creates a model 
AND generates and sends a tweet 
AND closes connections.
'''&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a large company, many people are joining and changing teams. New people have to understand obscure side effects. A company with less-readable code will waste days of cumulative onboarding time. A large docstring will not help: the longer the doc, the greater the chance it will be ignored completely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hard to release and debug
&lt;/h2&gt;

&lt;p&gt;The code with side effects interacts with stuff which is not under your direct control. After thinking about all imaginable failure modes of &lt;code&gt;abs_effectful&lt;/code&gt;, you might still find that it crashes after the release.&lt;/p&gt;

&lt;p&gt;There is a crazy variability in the external world. It is tough to reproduce, debug and fix all edge cases of side effects. Often it happens stochastically, and you just can't reproduce a single crash in-house (e.g., maybe when &lt;code&gt;abs_effectful&lt;/code&gt; runs on an old Windows version, GPU kernels behave differently?).&lt;/p&gt;

&lt;p&gt;The code with side effects takes much longer to release and debug.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hard to test
&lt;/h2&gt;

&lt;p&gt;Writing exhaustive unit tests for code with side effects is very hard. On top of the main result of &lt;code&gt;abs_stateful&lt;/code&gt;, you should better test that all side effects behave as expected: did the person get a tweet? Was the tweet parsed correctly? Not to say of some weird unit test interactions: side effects from one test could make other tests fail.&lt;/p&gt;

&lt;p&gt;Interacting with the external world on a test server is often impossible. Developers have to resort to mocking all external entities. See more in this &lt;a href="https://blog.thecodewhisperer.com/permalink/you-dont-hate-mocks-you-hate-side-effects"&gt;beautiful post&lt;/a&gt; from CodeWhisperer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hard to reuse
&lt;/h2&gt;

&lt;p&gt;What if your colleague wants to compute the absolute value of a number? There is no problem with &lt;code&gt;abs&lt;/code&gt;. You just import the function and use it! What about &lt;code&gt;abs_effectful&lt;/code&gt;? They wouldn't know a priori about any side effects (ignoring the hint in the name :). The code can happily work until some unfortunate day when some crazy issues from above start coming up.&lt;/p&gt;

&lt;p&gt;After finding the root cause, they'll anticipate all the problems we mentioned. Your colleague will just leave such a function alone and write their own simpler version.&lt;/p&gt;

&lt;p&gt;A codebase that relies on side effects would typically have much less code reuse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Hopefully, you’re convinced that side effects are also the root of all evil (just like &lt;a href="https://web.archive.org/web/20130731202547/http://pplab.snu.ac.kr/courses/adv_pl05/papers/p261-knuth.pdf"&gt;premature optimization&lt;/a&gt;). Now here is a compilation of all the reasons above:&lt;/p&gt;

&lt;p&gt;Code with side effects &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;is fragile - leads to unexpected crashes&lt;/li&gt;
&lt;li&gt;is unexpectedly code and is hard to optimize&lt;/li&gt;
&lt;li&gt;is hard to read and understand, it is hard to reuse&lt;/li&gt;
&lt;li&gt;is hard to debug and write tests&lt;/li&gt;
&lt;li&gt;is hard to use concurrently.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more discussion, please see the following great thread on &lt;a href="https://softwareengineering.stackexchange.com/questions/15269/why-are-side-effects-considered-evil-in-functional-programming"&gt;stackexchange&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Meanwhile, thank you for reading!&lt;/p&gt;

</description>
      <category>python</category>
      <category>programming</category>
      <category>architecture</category>
      <category>codenewbie</category>
    </item>
    <item>
      <title>Neat Python API</title>
      <dc:creator>Oleg Sinavski</dc:creator>
      <pubDate>Fri, 24 Feb 2023 23:42:00 +0000</pubDate>
      <link>https://forem.com/olegsinavski/neat-python-api-42aa</link>
      <guid>https://forem.com/olegsinavski/neat-python-api-42aa</guid>
      <description>&lt;p&gt;I've seen many Python interfaces in my career (&lt;a href="https://www.quora.com/How-do-API-and-GUI-differ" rel="noopener noreferrer"&gt;as in API, not UI&lt;/a&gt;).&lt;br&gt;
You can quickly spot an ugly one by its size. Below are some recipes on how to make a neat one!&lt;/p&gt;
&lt;h2&gt;
  
  
  Interfaces in Python
&lt;/h2&gt;

&lt;p&gt;An "interface" is a nebulous concept that might be initially hard to grasp.&lt;br&gt;
It is a blueprint of interactions with an object (reading a few answers &lt;a href="https://stackoverflow.com/questions/2866987/what-is-the-definition-of-interface-in-object-oriented-programming" rel="noopener noreferrer"&gt;here&lt;/a&gt; is not going to hurt). &lt;/p&gt;

&lt;p&gt;Do you need interfaces in Python at all? There are several good posts about it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://chelseatroy.com/2020/11/14/why-use-or-not-use-interfaces-in-python/" rel="noopener noreferrer"&gt;Chelsea Troy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://stackoverflow.com/questions/2124190/how-do-i-implement-interfaces-in-python" rel="noopener noreferrer"&gt;Stack Overflow&lt;/a&gt; &lt;em&gt;(please read several answers!)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://towardsdatascience.com/python-interfaces-why-should-a-data-scientist-care-2ed7ff80f225" rel="noopener noreferrer"&gt;Diego Barba&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’m going to assume that you read all that and decided to go with interfaces.&lt;br&gt;
I think &lt;code&gt;Protocols&lt;/code&gt; are &lt;a href="https://dev.to/post/1_abc_vs_protocols/"&gt;the best way to create interfaces&lt;/a&gt; in Python. This is what we are going to use here.&lt;/p&gt;

&lt;p&gt;Here, we are going to discuss what makes a &lt;strong&gt;good&lt;/strong&gt; interface.&lt;/p&gt;
&lt;h2&gt;
  
  
  An interface has to be small
&lt;/h2&gt;

&lt;p&gt;You’re designing a Zoo simulator and made an interface for "a generic animal":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Protocol&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;current_weight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;price&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;eat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;food&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;look_for_food&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hunger_level&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_asleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_awake&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;current_position&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;current_orientation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;RotationMatrix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;current_transform3d&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;HomogeneousMatrix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should be alarmed when you see an interface with more than 4-5 methods.&lt;br&gt;
Chances are that it was not well-designed, and the project it is used in will be overly convoluted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;this interface will be used in completely unrelated parts of the software. It will couple them together&lt;/li&gt;
&lt;li&gt;there will be extensive use of class hierarchies&lt;/li&gt;
&lt;li&gt;interface implementations are going to be overly complicated&lt;/li&gt;
&lt;li&gt;it will be hard to document and confusing to use.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You might think: “Clearly, animals do tons of different things, so it's justified to have many methods.”&lt;br&gt;
But you can come up with ten more methods to add to a “generic animal,” and there is never going to be an end to it.&lt;br&gt;
The art is to model a complicated thing with a set of small decoupled interfaces.&lt;/p&gt;

&lt;p&gt;What are the recipes for reducing an interface's size?&lt;/p&gt;
&lt;h2&gt;
  
  
  Semantic overlap
&lt;/h2&gt;

&lt;p&gt;Sometimes there could be some obvious duplication methods (e.g., &lt;code&gt;get_weights&lt;/code&gt; vs. &lt;code&gt;current_weight&lt;/code&gt;). &lt;br&gt;
But more often you'll find methods that somewhat overlap in semantics. &lt;br&gt;
After a bit of thought, it's possible to remove some of them. &lt;br&gt;
In the example above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;current_position&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
       &lt;span class="k"&gt;pass&lt;/span&gt;

   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;current_orientation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;RotationMatrix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="k"&gt;pass&lt;/span&gt;

   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;current_transform3d&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;HomogeneousMatrix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The user extract position and orientation from the &lt;code&gt;HomogeneousMatrix&lt;/code&gt; returned by &lt;code&gt;current_transform3d&lt;/code&gt;. &lt;br&gt;
The other two getters could be replaced by &lt;a href="https://alexsm.com/homogeneous-transforms/" rel="noopener noreferrer"&gt;utility functions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A similar situation is probably happening with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_asleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="k"&gt;pass&lt;/span&gt;

   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_awake&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Is it true that &lt;code&gt;animal.is_asleep() == not animal.is_awake()&lt;/code&gt;? If yes, then you better remove one of them. &lt;/p&gt;

&lt;h2&gt;
  
  
  Decoupling interfaces
&lt;/h2&gt;

&lt;p&gt;After removing duplicates, you should try to split the interface into independent parts. &lt;br&gt;
Most likely, you don’t use &lt;em&gt;all&lt;/em&gt; &lt;code&gt;Animal&lt;/code&gt; features in every part of your software:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;1.&lt;/em&gt; there could be a function that handles the drawing of animals that uses only &lt;code&gt;current_transform3d&lt;/code&gt; and &lt;code&gt;draw&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;draw_entity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current_transform3d&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;some_drawing_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;2.&lt;/em&gt; there could be an animal life cycle algorithm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;life_management&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;eating_logic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hunger_level&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;look_for_food&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; 
      &lt;span class="n"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eat&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="nf"&gt;sleeping_logic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_awake&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;3.&lt;/em&gt; and a Zoo management system&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;purchase_decision&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;budget&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
   &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current_weight&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;price&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="nf"&gt;decide_to_buy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;budget&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The “generic animal” is a collection of quite different interfaces that could be disentangled:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;VisualEntity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Protocol&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;current_transform3d&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;HomogeneousMatrix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BehavingAgent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;eat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;food&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;look_for_food&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hunger_level&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_awake&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ZooAsset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;current_weight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;price&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can see the advantages of many small interfaces over a single big one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You to decouple parts of your codebase and company workflows. A rendering team will not depend on Zoo management developers.&lt;/li&gt;
&lt;li&gt;No need to implement unnecessary methods for slight variations of “Animals.” Changing a price algorithm does not lead to a new species!&lt;/li&gt;
&lt;li&gt;You would be able to reuse small interfaces and utilities. You might find that the draw_entity and purchase_decision work for your Plant object.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now you can see the advantages of many small interfaces over a single big one.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They decouple parts of your codebase (while the large ones have a high chance of coupling unrelated parts). This in turn decouples company workflows (e.g. teams don't break each other's stuff too much)&lt;/li&gt;
&lt;li&gt;You don't have to write many implementations when instantiating a short interface. This ripples through your design because now you more easily avoid base classes and class hierarchies. Which in turn promotes composition over inheritance (there are many resources on why it's a good idea, e.g. see &lt;a href="https://stackoverflow.com/questions/49002/prefer-composition-over-inheritance" rel="noopener noreferrer"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;You would be able to reuse small interfaces in more places than large ones. This in turn makes the total surface area of APIs in your codebase smaller, easier to learn with simpler onboarding&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Recursive semantics
&lt;/h2&gt;

&lt;p&gt;Next, you should be alarmed if one function calls another in the same interface. &lt;br&gt;
Continuing with the Zoo example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BehavingAnimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Protocol&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lifecycle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
       &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt; What happens during 24 hours period &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;eat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;food&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
       &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt; What happens when the animal consumes food &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
       &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt; What happens when the animal sleeps &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is unlikely that an animal will spend a day without eating and sleeping.&lt;br&gt;
So there is a high chance that &lt;code&gt;eat&lt;/code&gt; and &lt;code&gt;sleep&lt;/code&gt; will be called from the &lt;code&gt;lifecycle&lt;/code&gt; implementation.&lt;/p&gt;

&lt;p&gt;Such an interface couples together two levels of abstraction. &lt;br&gt;
These are two interfaces in one, similar to the previous section. &lt;br&gt;
A "lifecycle" part is used in some global application context, but &lt;code&gt;eat&lt;/code&gt; and &lt;code&gt;sleep&lt;/code&gt; are probably used only locally inside it. &lt;br&gt;
Let's split them apart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ElementaryAnimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Protocol&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;eat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;food&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LifecycleManagement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Protocol&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lifecycle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ElementaryAnimal&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that now &lt;code&gt;lifecycle&lt;/code&gt; takes &lt;code&gt;ElementaryAnimal&lt;/code&gt; as an argument. &lt;br&gt;
This clearly states that a lifecycle depends on something that can eat and sleep. &lt;/p&gt;
&lt;h2&gt;
  
  
  Too generic
&lt;/h2&gt;

&lt;p&gt;Yet another thing to avoid is an overly-generic interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Creature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Protocol&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;act&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt; Do anything you want &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;perform_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;creature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Creature&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
     &lt;span class="n"&gt;custom_args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
     &lt;span class="n"&gt;creature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;act&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;custom_args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An interface is like a contract between two parties. &lt;br&gt;
But a contract that says “do whatever” is as good as no contract at all. &lt;br&gt;
It is too easy to make such interfaces in an optional/dynamic-typed language like Python, especially with its wildcard argument features.&lt;/p&gt;

&lt;p&gt;Unfortunately, such interfaces come up in some situations, but we can summarize it as “passing a black box around.” &lt;br&gt;
Passing “blackboxes” is typically worse than explicit components. &lt;br&gt;
But if you have to do it, it’s better to acknowledge its "blackness" by passing &lt;br&gt;
a functor and moving to a more functional design (here is an &lt;a href="https://medium.com/@olxc/switching-from-oop-to-functional-programming-4187698d4d3" rel="noopener noreferrer"&gt;in-depth tutorial&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;perform_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Callable&lt;/span&gt;&lt;span class="p"&gt;[...,&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
    &lt;span class="n"&gt;custom_args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
    &lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;custom_args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Constructors in an interface
&lt;/h2&gt;

&lt;p&gt;Related bonus topic: have you noticed that &lt;code&gt;__init__&lt;/code&gt; is typically &lt;strong&gt;not&lt;/strong&gt; a part of any interface? &lt;br&gt;
Why not? It seems like it is just another method you can have. &lt;/p&gt;

&lt;p&gt;It goes back to C++, where constructors are not virtual. &lt;br&gt;
Bjarne Stroustrup &lt;a href="https://www.stroustrup.com/bs_faq2.html#virtual-ctor" rel="noopener noreferrer"&gt;gives an answer&lt;/a&gt; on why it is so:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“A virtual call is a mechanism to get work done given partial information. &lt;br&gt;
To create an object, you need complete information. Consequently, a "call to a constructor" cannot be virtual.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I like high-level reasoning more: a constructor is a function for making objects. It belongs to the “object-makers” realm.&lt;br&gt;
It just can’t belong to an already-made object: you shouldn't make an object out of itself and I'm not a fan of &lt;a href="https://en.wikipedia.org/wiki/Prototype_pattern" rel="noopener noreferrer"&gt;cloning&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;A constructor could be viewed as a method of &lt;a href="https://en.wikipedia.org/wiki/Factory_(object-oriented_programming)" rel="noopener noreferrer"&gt;a factory&lt;/a&gt; interface&lt;br&gt;
(similar ideas can be found &lt;a href="https://stackoverflow.com/a/5367884" rel="noopener noreferrer"&gt;here&lt;/a&gt;).&lt;br&gt;
In other words, a constructor and an object interface are yet another example of two coupled levels of abstraction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;To summarize, a &lt;strong&gt;good&lt;/strong&gt; interface should:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;be small and concise&lt;/li&gt;
&lt;li&gt;avoid semantic duplication&lt;/li&gt;
&lt;li&gt;avoid coupling several parts in one&lt;/li&gt;
&lt;li&gt;avoid mixing multiple abstraction levels&lt;/li&gt;
&lt;li&gt;be specific and not too generic&lt;/li&gt;
&lt;li&gt;not include &lt;code&gt;__init__&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the same time, real life has many exceptions and complications. &lt;br&gt;
But those must be justified exceptions, not excuses to keep messy interfaces around.&lt;br&gt;
Thank you for reading!&lt;/p&gt;

&lt;p&gt;You can find me on &lt;a href="https://www.linkedin.com/in/oleg-sinavski/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://twitter.com/SinavskiOleg" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://sinavski.com/post/2_good_interface/" rel="noopener noreferrer"&gt;https://sinavski.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Python interfaces: abandon ABC and switch to Protocols</title>
      <dc:creator>Oleg Sinavski</dc:creator>
      <pubDate>Tue, 31 Jan 2023 21:34:56 +0000</pubDate>
      <link>https://forem.com/olegsinavski/python-interfaces-with-protocols-time-to-ditch-abc-51kp</link>
      <guid>https://forem.com/olegsinavski/python-interfaces-with-protocols-time-to-ditch-abc-51kp</guid>
      <description>&lt;p&gt;I used a standard Python library &lt;code&gt;abc&lt;/code&gt; to define interfaces for the last 10 years of my career. &lt;br&gt;
But recently, I found that relatively new Python &lt;a href="https://peps.python.org/pep-0544/" rel="noopener noreferrer"&gt;Protocols&lt;/a&gt; are way nicer. &lt;br&gt;
People find uses for both technologies. &lt;br&gt;
But I want to convince you to completely jump ships and start using them instead of more traditional techniques.&lt;/p&gt;
&lt;h2&gt;
  
  
  Python interfaces: what can you use?
&lt;/h2&gt;

&lt;p&gt;Python is somewhat different from other popular languages since there are no interfaces on a language level. &lt;br&gt;
However, there are several library implementations: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;abc&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;typing.Protocols&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;third-party implementations like &lt;a href="https://zopeinterface.readthedocs.io/en/latest/README.html#declaring-interfaces" rel="noopener noreferrer"&gt;Zope&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;custom implementations (e.g. &lt;a href="https://realpython.com/python-interface/" rel="noopener noreferrer"&gt;via metaclasses&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;abc&lt;/code&gt; package is probably the most popular:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;abc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;abstractmethod&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;eat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;food&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="k"&gt;pass&lt;/span&gt;

   &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, the most frequently mentioned package seems to be &lt;a href="https://zopeinterface.readthedocs.io/en/latest/README.html#declaring-interfaces" rel="noopener noreferrer"&gt;Zope&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;zope.interface&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Interface&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Interface&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;eat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;food&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="k"&gt;pass&lt;/span&gt;

   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Zope is a web-related library, and its interfaces have a lot of advanced features.&lt;/p&gt;

&lt;p&gt;Also, there are more custom packages and tutorials on the web on how to make an interface system yourself (&lt;a href="https://realpython.com/python-interface/" rel="noopener noreferrer"&gt;see example&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Finally, there are protocols:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Protocol&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Protocol&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;eat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;food&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="bp"&gt;...&lt;/span&gt;

   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A protocol is a formalization of Python's &lt;a href="https://en.wikipedia.org/wiki/Duck_typing" rel="noopener noreferrer"&gt;“duck-typing”&lt;/a&gt; ideology. There are many great articles on structural typing in Python&lt;br&gt;
(&lt;a href="https://trm.io/2017/01/29/structural-subtyping-python" rel="noopener noreferrer"&gt;this&lt;/a&gt;, &lt;a href="https://rednafi.github.io/reflections/structural-subtyping-in-python.html" rel="noopener noreferrer"&gt;that&lt;/a&gt;&lt;br&gt;
and some &lt;a href="https://news.ycombinator.com/item?id=26507161" rel="noopener noreferrer"&gt;discussion&lt;/a&gt;). &lt;br&gt;
Maybe protocols and interfaces are theoretically different beasts, but a protocol does the job.&lt;br&gt;
I had great success replacing &lt;code&gt;abc&lt;/code&gt; with &lt;code&gt;Protocols&lt;/code&gt; without any downsides.&lt;/p&gt;
&lt;h2&gt;
  
  
  What should you use?
&lt;/h2&gt;

&lt;p&gt;You should know that an interface system will not be localized to a small part of your codebase. &lt;br&gt;
After you choose to go with one, you’ll see it everywhere, and it's going to be hard to change in the future. &lt;br&gt;
So I would immediately dismiss any custom implementations or &lt;code&gt;Zope&lt;/code&gt;. &lt;br&gt;
It’s an extra dependency you have to deal with forever: installation, versions, support, and so on. &lt;br&gt;
For example, you have to install a &lt;a href="https://github.com/Shoobx/mypy-zope#plugin-for-mypy-to-support-zopeinterface" rel="noopener noreferrer"&gt;Mypy plugin&lt;/a&gt; to support a &lt;code&gt;zope.interface&lt;/code&gt; well.&lt;br&gt;
Additionally, a new developer in the team might not know this custom package, and you'll have to explain what it is and why you chose it.&lt;/p&gt;

&lt;p&gt;The main battle will happen between &lt;code&gt;abc&lt;/code&gt; and &lt;code&gt;Protocols&lt;/code&gt;. &lt;br&gt;
But if you really want a &lt;code&gt;zope&lt;/code&gt; vs &lt;code&gt;Protocols&lt;/code&gt; battle, please &lt;a href="https://glyph.twistedmatrix.com/2021/03/interfaces-and-protocols.html#runtime-support" rel="noopener noreferrer"&gt;read this&lt;/a&gt;&lt;br&gt;
(it has a detailed analysis of the runtime benefits of &lt;code&gt;zope&lt;/code&gt;).&lt;/p&gt;
&lt;h3&gt;
  
  
  Static checking first
&lt;/h3&gt;

&lt;p&gt;The big assumption I’m going to make is that you’re already convinced that static checking is a &lt;em&gt;must&lt;/em&gt;: &lt;br&gt;
you are not going to run the code that fails &lt;code&gt;pylint/mypy&lt;/code&gt;. &lt;br&gt;
Both checkers support &lt;code&gt;abc&lt;/code&gt; and &lt;code&gt;Protocols&lt;/code&gt; equally well. &lt;br&gt;
Also, just know that both &lt;code&gt;abc&lt;/code&gt; and &lt;code&gt;Protocols&lt;/code&gt; allow runtime checking, in case you need it.&lt;/p&gt;
&lt;h3&gt;
  
  
  Both support explicit syntax
&lt;/h3&gt;

&lt;p&gt;First, note that you still can explicitly inherit from an &lt;code&gt;abc&lt;/code&gt; &lt;em&gt;and&lt;/em&gt; a &lt;code&gt;Protocol&lt;/code&gt;. Many arguments in&lt;br&gt;
a &lt;a href="https://www.youtube.com/watch?v=xvb5hGLoK0A" rel="noopener noreferrer"&gt;very good video&lt;/a&gt; (and comments) from &lt;a href="https://www.arjancodes.com/" rel="noopener noreferrer"&gt;Arjan&lt;/a&gt; revolve around the misconception &lt;br&gt;
that you can’t do that with protocols. You totally can:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Giraffe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So in that regard, &lt;code&gt;abc&lt;/code&gt; and &lt;code&gt;protocols&lt;/code&gt; could be used the same way. &lt;br&gt;
However, Protocols give you an extra degree of design freedom by default.&lt;br&gt;
You can avoid explicit inheritance but still enjoy full interface checking:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Giraffe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# no base class needed!
&lt;/span&gt;   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;eat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;food&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mf"&gt;0.&lt;/span&gt;

   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mf"&gt;1.&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;feed_animal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt; 

&lt;span class="n"&gt;giraffe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Giraffe&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;feed_animal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;giraffe&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows you to make an interface for the code you don’t control and loosen the dependencies between modules in your codebase.&lt;br&gt;
Whether to choose an implicit or explicit option is a subtle choice decided on a case-by-case basis.&lt;br&gt;
A good example in favour of explicit "opt-in" for an interface is described &lt;a href="https://glyph.twistedmatrix.com/2021/03/interfaces-and-protocols.html#where-interfaces-work-best-hidden-and-complex-meanings" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;br&gt;
Protocols do not &lt;em&gt;force&lt;/em&gt; you to opt-in, but you can establish a company-wide rule to explicitly inherit from any protocol.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;abc&lt;/code&gt; also support implicit interfaces through the concept of &lt;a href="https://docs.python.org/3/library/abc.html#abc.ABCMeta.register" rel="noopener noreferrer"&gt;"virtual subclasses"&lt;/a&gt;.&lt;br&gt;
But you have to call &lt;code&gt;register&lt;/code&gt; for every implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Giraffe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# no base class needed!
&lt;/span&gt;   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;eat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;food&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mf"&gt;0.&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;

&lt;span class="n"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Giraffe&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# achieves the same as implicit Protocol
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Procotol&lt;/code&gt; supports implicit and explicit variants without extra syntax and works with &lt;code&gt;mypy&lt;/code&gt;.&lt;br&gt;
Also, &lt;a href="https://github.com/python/mypy/issues/2922" rel="noopener noreferrer"&gt;&lt;code&gt;mypy&lt;/code&gt; does not support &lt;code&gt;register&lt;/code&gt;&lt;/a&gt; as of the end of 2022.&lt;br&gt;
I'm not sure if we can fully count that in favour of &lt;code&gt;abc&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Protocols allow you to define an interface for a &lt;em&gt;function&lt;/em&gt; (not only a class) - see &lt;a href="https://mypy.readthedocs.io/en/stable/protocols.html#callback-protocols" rel="noopener noreferrer"&gt;callback protocols&lt;/a&gt;.&lt;br&gt;
It is a very cool feature that is worthy of a separate post.&lt;/p&gt;
&lt;h3&gt;
  
  
  Both support default methods :(
&lt;/h3&gt;

&lt;p&gt;Unfortunately, there is a big downside to both &lt;code&gt;abc&lt;/code&gt; and &lt;code&gt;Protocols&lt;/code&gt;.&lt;br&gt;
In the real world, many people work in a single codebase. &lt;br&gt;
Abstract base classes sometimes tend to acquire default method implementations.&lt;br&gt;
This is what it might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Protocol&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  &lt;span class="c1"&gt;# the same holds for Animal(ABC):
&lt;/span&gt;   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;eat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;food&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="p"&gt;...&lt;/span&gt;  &lt;span class="c1"&gt;# this is still abstract
&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mf"&gt;3.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In that case, they stop being “abstract” and become just base classes.&lt;br&gt;
Python and static checkers do not catch that.&lt;br&gt;
A software design with inheritance is not really the same as a design with interfaces. &lt;br&gt;
I would love Python to separate them on a language level, but it is unlikely to happen. &lt;br&gt;
Implicit protocols have an advantage here. &lt;br&gt;
They allow you to avoid messy inheritance altogether.&lt;/p&gt;

&lt;h3&gt;
  
  
  Protocols are shorter
&lt;/h3&gt;

&lt;p&gt;Last but not least, you can count the number of lines of code you need to define an interface.&lt;br&gt;
With &lt;code&gt;abc&lt;/code&gt;, you must have an &lt;code&gt;abstractmethod&lt;/code&gt; decorator for every method.&lt;br&gt;
But with Protocols without runtime checking, you don't have to use any decorators at all.&lt;br&gt;
So here, Protocols win hands down.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Let’s add up the scores:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;ABC&lt;/th&gt;
&lt;th&gt;Protocols&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Runtime checking&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Static checking&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Explicit interface with inheritance&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Implicit interface without inheritance (&lt;code&gt;abc&lt;/code&gt; requires &lt;code&gt;register&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;0.5&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ability to have default method implementation&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Callback interface&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Number of lines&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total&lt;/td&gt;
&lt;td&gt;1.5&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Hopefully, I’m not missing anything huge in this analysis. Thank you for reading!&lt;br&gt;
Looking at the results, team &lt;strong&gt;"Protocols"&lt;/strong&gt; wins, and you probably should just start using it! &lt;/p&gt;

&lt;p&gt;Thank you for reading! You can find me on &lt;a href="https://www.linkedin.com/in/oleg-sinavski/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://twitter.com/SinavskiOleg" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://sinavski.com" rel="noopener noreferrer"&gt;https://sinavski.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>career</category>
      <category>codenewbie</category>
    </item>
  </channel>
</rss>
