<?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: Ivan Zakutnii</title>
    <description>The latest articles on Forem by Ivan Zakutnii (@m0n0x41d).</description>
    <link>https://forem.com/m0n0x41d</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%2F1189699%2Fbbe54712-d7b9-4b0f-9474-b7602ebcd6d3.jpg</url>
      <title>Forem: Ivan Zakutnii</title>
      <link>https://forem.com/m0n0x41d</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/m0n0x41d"/>
    <language>en</language>
    <item>
      <title>🎰 Stop Gambling with Vibe Coding: Meet Quint</title>
      <dc:creator>Ivan Zakutnii</dc:creator>
      <pubDate>Wed, 17 Dec 2025 21:20:49 +0000</pubDate>
      <link>https://forem.com/m0n0x41d/stop-gambling-with-vibe-coding-meet-quint-1f3j</link>
      <guid>https://forem.com/m0n0x41d/stop-gambling-with-vibe-coding-meet-quint-1f3j</guid>
      <description>&lt;p&gt;Let's be real for a second. Prompting Claude, Cursor, or ChatGPT feels amazing... until it doesn't.&lt;/p&gt;

&lt;p&gt;You know the cycle:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You type a vague prompt like "Make me a auth system."&lt;/li&gt;
&lt;li&gt;The AI spits out 200 lines of beautiful-looking TypeScript.&lt;/li&gt;
&lt;li&gt;You get that dopamine hit. "I am a 10x engineer!"&lt;/li&gt;
&lt;li&gt;You run it.&lt;/li&gt;
&lt;li&gt;ERROR: undefined is not a function.&lt;/li&gt;
&lt;li&gt;You spend the next 4 hours debugging code you didn't write and barely understand.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That’s not engineering. That’s a slot machine with syntax highlighting.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Problem: AI (and most of us frequently) Has No "Chill"
&lt;/h4&gt;

&lt;p&gt;Vibe coding is chaotic because LLMs are people-pleasers. They want to give you code now, regardless of whether it actually makes sense architecturally. They lack a Thinking Framework. They don't check invariants.&lt;/p&gt;

&lt;p&gt;They just... vibe.&lt;/p&gt;

&lt;p&gt;What if you could force the AI to actually think before it types?&lt;/p&gt;

&lt;h3&gt;
  
  
  Enter Quint 🛠️
&lt;/h3&gt;

&lt;p&gt;Quint is a tiny, no-nonsense toolkit for AI-assisted engineering.&lt;/p&gt;

&lt;p&gt;It’s not a new IDE. It’s not a bloated SaaS wrapper. It’s a set of CLI commands (currently) that act as a "Thinking OS" for your AI tool collaboration – making both of you more rigorous.&lt;/p&gt;

&lt;p&gt;Whether you use Claude Code, Cursor, Gemini CLI, or Codex, Quint sits in the middle and says: "Hey AI, before you write that function, prove to me it won't break the build."&lt;/p&gt;

&lt;h3&gt;
  
  
  Why You Should Care (Right Now)
&lt;/h3&gt;

&lt;p&gt;The current version is commands set only. No UI. No dependencies to break anything. Tiny, almost zero overhead in the context for your Claude Code or Cursor.&lt;/p&gt;

&lt;p&gt;But We’ve been using it in real scenarious of tackling highly complex engineering and even marketing tasks, and you know what... the difference in the results quality is wild. &lt;/p&gt;

&lt;p&gt;Instead of getting "plausible spaghetti," we get decision records that actually respect the goals and are linked with evidence.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;It just make sense.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  What's Under The Hood? 🧠
&lt;/h4&gt;

&lt;p&gt;The current latest version of Quint Code implements about 10% of the &lt;a href="https://github.com/ailev/FPF" rel="noopener noreferrer"&gt;First Principles Framework&lt;/a&gt; (FPF) – an original, brilliant but formal, and very complex specification of the "thinking OS" created by Anatoly Levenchuk.&lt;/p&gt;

&lt;p&gt;Now, I know what you're thinking: "Only 10%? Why release it?"&lt;/p&gt;

&lt;p&gt;Because that 10% is the Pareto Principle in action. It turns out you don't need a PhD in Formal Logic to improve AI and AI + Human collaborative reasoning. &lt;/p&gt;

&lt;p&gt;You just need to force the AI to acknowledge few Invariants and Reasoning Chains. And then act as External Transformer, like an Oracle or... Overseer.&lt;/p&gt;

&lt;p&gt;Even this minimal implementation forces AI agents to plan the decisions and thus the follow-up work much better than any heuristic planners and to-dos.&lt;/p&gt;

&lt;h4&gt;
  
  
  🔮 The Near Future: v4.0.0 &amp;amp; The MCP Hype Train
&lt;/h4&gt;

&lt;p&gt;We are close to dropping v4.0.0, and it’s going to be a banger.&lt;/p&gt;

&lt;p&gt;We're introducing a tiny MCP (Model Context Protocol) Server which will handle FPF kernel and Invariants better in local sqlite database + same MD files.&lt;/p&gt;

&lt;p&gt;It will allow Quint to feed the AI persistent context about your project's "Laws of Physics," as well as your rules and past decisions mostly automatically.&lt;/p&gt;

&lt;p&gt;We're aiming for ~75% FPF Invariants support with this MCP. Still small. Still focused. But dramatically smarter.&lt;/p&gt;

&lt;h4&gt;
  
  
  🧪 Try It. Break It. Roast It.
&lt;/h4&gt;

&lt;p&gt;I don't want "polite" feedback. I want feedback from devs who are actually in the trenches using Cursor/Claude Code daily. Who have zero ideas about this scientific stuff and all the systems engineering formalities and methods that I do love, but the devs, as I said - in the trenches. And I need the feedback from the trenches.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does it fit your flow?&lt;/li&gt;
&lt;li&gt;Is the README too confusing?&lt;/li&gt;
&lt;li&gt;Did it save you from a hallucination?&lt;/li&gt;
&lt;li&gt;Did it help you to plan complex tasks better?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🔗 Link to Quint Code Repo: &lt;a href="https://github.com/m0n0x41d/quint-code" rel="noopener noreferrer"&gt;https://github.com/m0n0x41d/quint-code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Go ahead. Try it out. If it sucks, tell me why. If it fixes your vibe coding hangover, tell your friends.&lt;/p&gt;

&lt;p&gt;Quint is a small tool, but it has a damn big brain energy.&lt;/p&gt;




&lt;p&gt;Thanks for reading,&lt;br&gt;
ivan zakutni&lt;/p&gt;

</description>
      <category>vibecoding</category>
      <category>ai</category>
      <category>systemsthinking</category>
      <category>claudecode</category>
    </item>
    <item>
      <title>Exception Generation is Pure Evil</title>
      <dc:creator>Ivan Zakutnii</dc:creator>
      <pubDate>Tue, 05 Aug 2025 18:47:02 +0000</pubDate>
      <link>https://forem.com/m0n0x41d/exception-generation-is-pure-evil-5bda</link>
      <guid>https://forem.com/m0n0x41d/exception-generation-is-pure-evil-5bda</guid>
      <description>&lt;p&gt;Exception generation. You certainly know what this is and have definitely used it, if you've programmed even a little.&lt;/p&gt;

&lt;p&gt;Here's news for you: exception generation is very often bad.&lt;/p&gt;

&lt;p&gt;In any situation where a &lt;code&gt;try/except&lt;/code&gt; (or try/catch) block is used not for "wrapping" side effects like reading files, going to the network, or in an isolated place around a function (for targeted debugging purposes or simply because "there's just no other way") — it's better to get rid of it.&lt;/p&gt;

&lt;p&gt;But why? Because such &lt;code&gt;try/except&lt;/code&gt; blocks seriously confuse two "processes":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Program execution flow&lt;/li&gt;
&lt;li&gt;Developer's thought flow who will read this code&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Think about it yourself — &lt;em&gt;any&lt;/em&gt; instruction in a try/except block can throw an exception.&lt;/p&gt;

&lt;p&gt;Does it seem like each &lt;em&gt;such&lt;/em&gt; block increases the complexity of our program's execution flow graph &lt;em&gt;exponentially&lt;/em&gt;? It doesn't just seem so.&lt;/p&gt;

&lt;p&gt;Modern static analyzers experience... &lt;a href="https://www.sciencedirect.com/topics/computer-science/control-flow-analysis" rel="noopener noreferrer"&gt;some difficulties&lt;/a&gt; with "invisible execution paths".&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Don't miss from me: &lt;a href="https://linktr.ee/m0n0x41d" rel="noopener noreferrer"&gt;https://linktr.ee/m0n0x41d&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h4&gt;
  
  
  Brief historical background.
&lt;/h4&gt;

&lt;p&gt;People started talking about exceptions a very long time ago — in the 40s-50s. The first software implementation appeared in LISP, where there were two functions — &lt;code&gt;ERROR&lt;/code&gt;, which was called in case of a program failure, and &lt;code&gt;ERRORSET&lt;/code&gt;, which returned, attention — either an &lt;em&gt;error code&lt;/em&gt; or &lt;code&gt;NIL&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In other words, this story in LISP is not about the exceptions we have now at all... The concept and implementation evolved, evolved, and developed to the point where instead of error codes we started &lt;em&gt;generating&lt;/em&gt; exceptions, namely — &lt;em&gt;creating exception conditions&lt;/em&gt; and handling them &lt;strong&gt;separately&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The period of mass distribution of exception generation fell on the 80s-90s.&lt;/p&gt;

&lt;p&gt;And here's the result. From a &lt;a href="https://users.ece.cmu.edu/~koopman/des_s99/exceptions/" rel="noopener noreferrer"&gt;study&lt;/a&gt; by Carnegie Mellon from 1999:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Exception failures are estimated to cause two thirds of system crashes and fifty percent of computer system security vulnerabilities. Exception handling is especially important in embedded and real-time computer systems because software in these systems cannot easily be fixed or replaced, and they must deal with the unpredictability of the real world. Robust exception handling in software can improve software fault tolerance and fault avoidance, &lt;strong&gt;but no structured techniques exist for implementing dependable exception handling.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'm writing this article in 2025, and there's still no structured, reliable, implemented method for exception handling.&lt;/p&gt;

&lt;p&gt;Mainstream OOP languages provide the same old try/catch, and frameworks spread several layers of abstractions on top in an even layer, further blurring error boundaries, often providing developers with no ways to handle errors except... wrapping it all in yet another try/catch.&lt;/p&gt;

&lt;p&gt;On top of that, exception generation can seriously slow down performance. Here's fascinating &lt;a href="https://www.baeldung.com/java-exceptions-performance" rel="noopener noreferrer"&gt;reading&lt;/a&gt; from the Java world. Though it's not limited to just Java — &lt;a href="https://johnfarrier.com/c-error-handling-strategies-benchmarks-and-performance/" rel="noopener noreferrer"&gt;here's&lt;/a&gt; an article about experiments in C++. In Python, especially with 3.11, the situation seems not so dramatic, &lt;em&gt;occurring&lt;/em&gt; exceptions slow down code by &lt;em&gt;just 3-5 times&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I'll just leave &lt;a href="https://docs.python.org/2/faq/design.html#how-fast-are-exceptions" rel="noopener noreferrer"&gt;this&lt;/a&gt; here:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A &lt;code&gt;try/except&lt;/code&gt; block is extremely efficient &lt;strong&gt;if no exceptions are raised.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;They say that once upon a time, long, long ago, the grass was greener and programs were easier to verify. The times before the dominance of exception generation came were the times of Structured Programming — engineers wrote code using conditional operators, loops, and subroutines (functions with one entry and one exit).&lt;/p&gt;

&lt;p&gt;Programs flowed, developed naturally "top-down". Thanks to such determinism, they were easier to read both for people and machines — the first static code analyzer &lt;code&gt;lint&lt;/code&gt; was publicly released in 1979. Well, you already know what happened next.&lt;/p&gt;

&lt;h3&gt;
  
  
  What do we do with all this?
&lt;/h3&gt;

&lt;p&gt;You can act like &lt;a href="https://google.github.io/styleguide/cppguide.html#Exceptions" rel="noopener noreferrer"&gt;Google&lt;/a&gt; (especially if you write in C++):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We do not use C++ exceptions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In any other popular object-oriented language you need to... stop using exceptions :)&lt;/p&gt;

&lt;p&gt;Or &lt;em&gt;at least&lt;/em&gt; don't bury them deep in code — let the try/catch block wrap a function right where it's called (outside or inside). Yes, this might add some amount of boilerplate code, but then when exceptions occur, the execution flow will return faster to the deterministically beaten "path".&lt;/p&gt;

&lt;p&gt;Either way, we &lt;em&gt;can&lt;/em&gt; find a way to program without using exceptions for flow control.&lt;/p&gt;

&lt;p&gt;And you can also remember Golang. No matter how angular and dumbed down it might be, the biggest design decision aimed at &lt;em&gt;strengthening&lt;/em&gt; Go code produced by developers is forcing them to handle every error explicitly. Yes, Go has defer/panic/recover, but you'll have to work hard to twist and mangle the execution flow severely.&lt;/p&gt;

&lt;p&gt;And finally — functional programming. Result monad, for example. Curtain.&lt;br&gt;
                                                ¯\_(ツ)_/¯&lt;/p&gt;

&lt;p&gt;However, I think most developers will find it much easier to simply abandon leaking raise/throw — look at your try/except carefully — maybe you can use &lt;code&gt;if&lt;/code&gt; here? Look at &lt;code&gt;raise&lt;/code&gt; — how about &lt;code&gt;return&lt;/code&gt;?&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>errors</category>
    </item>
    <item>
      <title>Why Augmented Intelligence after all?</title>
      <dc:creator>Ivan Zakutnii</dc:creator>
      <pubDate>Wed, 28 May 2025 19:01:12 +0000</pubDate>
      <link>https://forem.com/m0n0x41d/why-augmented-intelligence-after-all-5118</link>
      <guid>https://forem.com/m0n0x41d/why-augmented-intelligence-after-all-5118</guid>
      <description>&lt;h2&gt;
  
  
  Why Augmented after all?
&lt;/h2&gt;

&lt;p&gt;Why, when talking about AI, do I almost always primarily mean Augmented, not Artificial Intelligence? &lt;/p&gt;

&lt;p&gt;What is Augmented Intelligence anyway? &lt;/p&gt;

&lt;p&gt;In translation, this would sound like – Augmented, not Artificial intelligence.&lt;/p&gt;

&lt;p&gt;It's quite difficult to establish the authors of the term, because the idea began developing long ago, long before the appearance of transformers, roughly around the same time when AI was first talked about as Artificial (late 60s, early 70s). &lt;/p&gt;

&lt;p&gt;Augmented Intelligence is talked about in so many ways – both as a system design pattern, and as an approach to "human enhancement," productivity improvement. The word Augmented appears in the context of theoretical LLM training methodologies and much more. Someone, for example, would rather remember AR, and someone might even think about Musk's neuralink.&lt;/p&gt;

&lt;p&gt;Therefore, in this post I want to talk about the interpretation from &lt;em&gt;my&lt;/em&gt; point of view, why I use this term, and how seriously I take it.&lt;/p&gt;

&lt;p&gt;My interpretation lies somewhere based on my experience, and still somewhat gravitates toward a "design pattern," we just need to figure out the &lt;em&gt;design&lt;/em&gt; and pattern of &lt;em&gt;what&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;TLDR;&lt;/p&gt;

&lt;p&gt;Artificial Intelligence – is about autonomous, independent agents that work completely without humans 🤘🏻&lt;br&gt;
Augmented Intelligence – is about a something like symbiosis of human and AI, where each strengthens the other 👤🤝🏻🤖&lt;/p&gt;

&lt;p&gt;In reality, the second (augmented) already works much more often and better than the first, is already much more present here than completely independent artificial. And as technology develops, the symbiosis mindset still looks more appealing than the "exploitation" mindset, even, not if – but when, artificial neural networks become even more cognitively powerful!&lt;/p&gt;

&lt;p&gt;Why exploitation? Because Artificial from the perspective of human "collective unconscious" is already fully forming as our species' relationship to AI as proprietary. Have you seen all those memes about bullying and threats against poor ChatGPT? &lt;/p&gt;

&lt;p&gt;Being the boss – might not be bad, but cultivating a boss mindset, in my opinion – is counterproductive.&lt;/p&gt;

&lt;p&gt;Let's return to my experience and move away from science fiction and anthropological speculation. &lt;/p&gt;

&lt;h2&gt;
  
  
  Mindset matters
&lt;/h2&gt;

&lt;p&gt;I've already started talking about mindset, and this is very important. Unfortunately or fortunately, AI is quite limited, especially as soon as we try to apply it in a narrow, vertical niche. &lt;/p&gt;

&lt;p&gt;When communicating with conditional ChatGPT or Claude, many people get the impression that "it's very smart," "it can do almost anything." This impression forms not only among individuals, but also among companies, entrepreneurs who attempt to build new business with LLM or start a new one altogether.&lt;/p&gt;

&lt;p&gt;Even quite experienced engineers and managers can find themselves believing that AI systems are something completely magical, quite simple in terms of implementation – "well, it's already smart, right? It'll figure out our mess somehow, and we'll live happily ever after!"&lt;/p&gt;

&lt;p&gt;As a result, another misconception follows – AI-related projects don't need to be thought through or modeled at all.&lt;/p&gt;

&lt;p&gt;In reality, this turns out to be a path to nowhere. Teams lightning-fast encounter difficulties and spend a long time learning the hard way, feeling out what and how LLM can do, and what it can't, ultimately coming to the fact that people have &lt;em&gt;one language&lt;/em&gt; in their heads, the model has a completely &lt;em&gt;different language&lt;/em&gt;, we'll still have to engage in ontological modeling of both the system itself and the language in which we communicate with each other and with the AI agent.&lt;/p&gt;

&lt;p&gt;It turns out that humans are needed in AI systems much more than expected... and here we smoothly approach Augmented.&lt;/p&gt;

&lt;h2&gt;
  
  
  Human as part of the system
&lt;/h2&gt;

&lt;p&gt;As long as AI hasn't become a recognized or even a form of life, we continue to collectively exist in human society. Knowing or at least suspecting AI's limitations, and &lt;em&gt;especially knowing about the exceptional potential&lt;/em&gt; of this technology, I believe it's correct to talk specifically about Augmented Intelligence as a way to effectively change our reality and ourselves for the better. &lt;/p&gt;

&lt;p&gt;Already now, more and more quality AI systems are appearing, but all these surviving systems are either extremely specific and aimed at performing concrete tasks, or complex systems, workflow pipelines, which necessarily have human(s) who at minimum service the system, but often also perform the role of observer and quality controller.&lt;/p&gt;

&lt;p&gt;Moreover, these systems themselves are obviously aimed at being used by end users – people, outside the organization as clients, or inside, if we're talking about AI Platform.&lt;/p&gt;

&lt;p&gt;I'm getting at the fact that even if we consider a scenario of a "very vertical" agent/AI system – it was still modeled, created by people, and serves people. Whatever AI is, however we consider it – it's &lt;em&gt;already&lt;/em&gt; closely linked with our large and small systems. &lt;/p&gt;

&lt;p&gt;What about systems that still manage to be "without LLM," and which might even manage to remain so for a long time, due to business specifics – here it still makes sense to talk about Augmented, because the efficiency of people working in such organizations can (and often already does) accelerate thanks to rational use of neural networks in their work. &lt;/p&gt;

&lt;h2&gt;
  
  
  Two scenarios of AI usage
&lt;/h2&gt;

&lt;p&gt;If we consider from the end user's perspective, AI has now become that very magic box, with which the user can start becoming very stupid (Artificial Intelligence mindset – the user almost completely delegates slow thinking to LLMs, and their cognitive abilities degrade), or develop, learn, get smarter, accelerate their work on routine tasks with the help of LLMs, quickly get feedback, strictly evaluate it – delegate a lot of fast thinking to LLM so it happens &lt;em&gt;even faster&lt;/em&gt;, some selected part of slow thinking, but continue to &lt;em&gt;make decisions&lt;/em&gt; (intermediate and final, as well as &lt;em&gt;apply mental effort&lt;/em&gt; to the task &lt;strong&gt;independently&lt;/strong&gt; (Augmented Intelligence mindset).&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;I think there's nothing wrong with creating more and more AI products, agents to help human agents or other AI agents.&lt;/p&gt;

&lt;p&gt;Wherever the evolution of artificial neural networks goes, I'm completely uninterested in speculating about doomsday and other "analytical" nonsense – obviously, we won't stop anymore, and benefits are still manifesting more than harm.&lt;/p&gt;

&lt;p&gt;Yes, there seems to be a threat of depriving many people of physical labor, well, maybe it's time to work more with brains and create? Or maybe on the contrary, more jobs will appear, of completely new quality, but which will still have to be adapted to in an Augmented manner?&lt;br&gt;
I don't know, in this blog I'm not going to waste my and your attention on these reflections, there's too much such material, and if you're interested – you'll easily find it.&lt;/p&gt;

&lt;p&gt;As I already said – Augmented Intelligence is about cooperation of different agents, strengthening each other. I want to believe that in this "design pattern" we'll start building systems in which we not only better cooperate with LLM, but also as human with human.&lt;/p&gt;

&lt;p&gt;Therefore, in my blog I write specifically about designing such "symbiotic" systems - where AI strengthens humans, at least even with the result of work on assigned tasks.&lt;/p&gt;

&lt;p&gt;I believe that the conversation about Augmented – is grounding in reality, in understanding systemicity and interconnections. This is a path of thinking toward &lt;em&gt;the conversation itself&lt;/em&gt; and efficiency overall. The conversation about Artificial – is too ephemeral, often speculative and entertaining, simply because &lt;em&gt;it happened that way&lt;/em&gt;, focusing on artificiality looks like directing attention only toward LLM, whereas in the conversation about Augmented we don't forget about ourselves, and about other agents that will work in our system.&lt;/p&gt;

&lt;p&gt;If you're building AI systems and want them to actually work in reality and in production - &lt;a href="//links.ivanzakutnii.com"&gt;welcome&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>systemsengineering</category>
      <category>agents</category>
    </item>
    <item>
      <title>Productivity through primary metrics: how to finally understand where your time goes</title>
      <dc:creator>Ivan Zakutnii</dc:creator>
      <pubDate>Tue, 15 Apr 2025 17:10:35 +0000</pubDate>
      <link>https://forem.com/m0n0x41d/productivity-through-primary-metrics-how-to-finally-understand-where-your-time-goes-20lj</link>
      <guid>https://forem.com/m0n0x41d/productivity-through-primary-metrics-how-to-finally-understand-where-your-time-goes-20lj</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.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%2Fft2iynlgvqldq21gensz.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fft2iynlgvqldq21gensz.jpeg" alt=" " width="800" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In a world where we're constantly offered new task management apps, time management techniques, and approaches to increase productivity, many of us find that despite all these tools, our real effectiveness barely changes.&lt;/p&gt;

&lt;p&gt;We implement GTD, practice Pomodoro, use Kanban boards, read about Atomic Habits and Japanese Kaizen — but somehow we continue to feel overwhelmed and not productive enough, and often abandon these ideas altogether.&lt;/p&gt;

&lt;p&gt;The main problem is that most productivity methods operate in a vacuum — they offer systems for organizing tasks but don't address the fundamental question: &lt;strong&gt;what exactly are we spending our time on in reality&lt;/strong&gt;. Without this basic understanding, even the most advanced methods remain just theoretical constructs that can't significantly change our lives.&lt;/p&gt;

&lt;p&gt;In this article, I propose a radically different approach to productivity, based on collecting and analyzing &lt;strong&gt;primary metrics&lt;/strong&gt; — objective data about how your time is actually distributed.&lt;/p&gt;

&lt;p&gt;We'll look at the two most important resources – time and attention.&lt;/p&gt;

&lt;p&gt;By honestly applying the practice I'm suggesting, you'll be able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;See the real picture of your time expenditure without illusions or self-deception&lt;/li&gt;
&lt;li&gt;Identify hidden sources of time loss that you might not be aware of&lt;/li&gt;
&lt;li&gt;Create a personalized, convenient productivity system based on your preferences&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also from this article, you'll learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why without primary metrics, any productivity system is almost certainly doomed to fail&lt;/li&gt;
&lt;li&gt;How I came to realize the importance of measuring time through personal experience&lt;/li&gt;
&lt;li&gt;A simple and flexible system for categorizing time that you can start using&lt;/li&gt;
&lt;li&gt;How to implement time tracking without turning it into an additional source of stress&lt;/li&gt;
&lt;li&gt;How to analyze the collected data and use it for real productivity improvements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don't worry — I'm not suggesting you become obsessed with every minute of your life. Instead, I'll share a balanced and fundamental approach.&lt;/p&gt;

&lt;p&gt;But all this will only work if you're truly ready to finally see where your &lt;em&gt;precious&lt;/em&gt; time is actually leaking away.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why we need primary metrics
&lt;/h2&gt;

&lt;p&gt;Primary metrics are extremely important whenever we truly strive to change any process for the better. Today we're looking at our own lives and work on any projects as the process.&lt;/p&gt;

&lt;p&gt;But first, I'll tell you my path to the proposed &lt;em&gt;system&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;For quite a long time, I used todo lists and something resembling GTD. The everyday life of a family with a small child in rented housing in a foreign country can be quite unpredictable, but todo lists have increased my productivity over the past year and a half.&lt;/p&gt;

&lt;p&gt;The more tasks I delegated to my todo list, the more computing power remained in my brain to perform work tasks more &lt;em&gt;focused&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;And I started to perform much more of them, so much so that I began to overwork in unhealthy proportions.&lt;/p&gt;

&lt;p&gt;When I realized that overworking was a big problem, I started using time tracking to account for the time I spend &lt;em&gt;on work&lt;/em&gt;. This was a purely intuitive decision; I was tired of overworking.&lt;/p&gt;

&lt;p&gt;Although such tracking didn't become a habit right away, over the course of several months I started working a reasonable amount of time.&lt;/p&gt;

&lt;p&gt;But I didn't feel like I had more resources for my own projects. I was still responding to any new ideas like this - "when would I ever have time for that?"&lt;/p&gt;

&lt;p&gt;I spent most of my working time in focus periods, measuring them using the pomodoro method.&lt;/p&gt;

&lt;p&gt;I was doing a lot, but I wasn't doing it productively. Although all work tasks were moving forward, almost all of my projects looked as if they were standing still. I didn't have time to regularly write a blog, think through and develop my side projects. I only had time to work, create new tasks for myself, study software engineering, and do something else &lt;em&gt;in bits and pieces&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The problem turned out to be that I wasn't trying to specifically ask myself the question - "where am I spending my time" and honestly find an answer to it.&lt;/p&gt;

&lt;p&gt;The answer came through &lt;em&gt;primary metrics&lt;/em&gt;. How do you collect these metrics?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Track time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Wait. But wasn't I measuring the time I spent on work? Yes, I was, but the goal wasn't to collect metrics and understand &lt;em&gt;where, what I'm actually spending time on&lt;/em&gt;, but simply to stop overworking.&lt;/p&gt;

&lt;p&gt;And I achieved this goal, but didn't achieve an overall improvement in productivity and quality of work across all my areas.&lt;/p&gt;

&lt;p&gt;I filled all the other time (outside of work) with tasks that I managed to grab from the first or second priorities of my todo list.&lt;/p&gt;

&lt;p&gt;Everything changed when I became familiar with the idea about the importance of primary metrics, the importance of grounding any ideas and theories &lt;em&gt;in reality&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In fact, the applied methodology of productivity and work organization is not so important. It's not important in the sense that there are many such methodologies, and different methodologies are well suited to different people.&lt;/p&gt;

&lt;p&gt;But no methodology will work properly if you don't realize &lt;em&gt;what you're actually working with, what's really happening? How much work and resources are being spent? How productive is it in terms of progress toward the result?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Our most valuable resource is time, and in order to take a step into a more productive future, you need to understand where you're spending this resource. Then you can choose whatever methods you like (or even develop your own) and use them effectively.&lt;/p&gt;




&lt;p&gt;Perhaps you're already scared now, and you have thoughts like this - &lt;em&gt;"it's absolute madness to track every activity by the hour! I'll go crazy, I'll just waste more time constantly recording these time intervals!"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You're absolutely right – just trying to track where you spend your time without a &lt;em&gt;system&lt;/em&gt; is difficult, will cause a lot of stress, and might be the opposite of the idea of efficiency.&lt;/p&gt;

&lt;p&gt;That's why I'm writing this guide article for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Time categorization system
&lt;/h2&gt;

&lt;p&gt;Today I'm offering you a categorization system that will help you collect metrics without much effort.&lt;/p&gt;

&lt;p&gt;Good news:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It's not difficult; you already have everything you need to get started&lt;/li&gt;
&lt;li&gt;You don't need to mark the time minute by minute, and you probably won't be able to. A drift of 15-30 minutes is more than acceptable to begin with&lt;/li&gt;
&lt;li&gt;I'm not suggesting you become a productivity maniac and track time for the rest of your life (this is an optional habit, and you're free to decide in the future whether you need it or not)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Bad news:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You'll have to work independently with what you discover. I'm not giving you a fish, but a fishing rod.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As you can see - there seems to be more good news, right? :)&lt;/p&gt;

&lt;h3&gt;
  
  
  First-class abstraction
&lt;/h3&gt;

&lt;p&gt;A good abstraction in software engineering is one that &lt;em&gt;generalizes&lt;/em&gt; rather than specifies.&lt;/p&gt;

&lt;p&gt;So, to get closer to a breakthrough in productivity, I suggest you try tracking how much time in your life you spend, divided into the following categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Productive time&lt;/li&gt;
&lt;li&gt;Investment time&lt;/li&gt;
&lt;li&gt;Body maintenance&lt;/li&gt;
&lt;li&gt;Everyday life and rest&lt;/li&gt;
&lt;li&gt;Time wasted&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's understand what these categories are.&lt;/p&gt;

&lt;h3&gt;
  
  
  Productive time
&lt;/h3&gt;

&lt;p&gt;Productive time is time you spent on &lt;em&gt;focused&lt;/em&gt; work on specific tasks (like work tickets from a Kanban board). This is time spent on tasks that bring you closer to achieving some already outlined, planned, and specific goal. For example, strategic planning can also be included here - because it's aimed at moving somewhere, it's clear which direction to "dig," we actually have a task like "Research X to understand how to do Z for project Y."&lt;/p&gt;

&lt;h3&gt;
  
  
  Investment time
&lt;/h3&gt;

&lt;p&gt;This is time you spend on self-education, general research work (searching for new ideas and opportunities), or on your side projects.&lt;/p&gt;

&lt;p&gt;Investment time is called that - not only because learning and research are truly investments in your future but also because in case of urgent need, this time can be "sacrificed" in favor of &lt;em&gt;productive time&lt;/em&gt; to complete a project or deal with some work emergencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Body maintenance
&lt;/h3&gt;

&lt;p&gt;Sleep, food (quick preparation, but not fast food). Normal food that can be prepared and consumed in a reasonable time, not 2-3 hours. If you decided to prepare a lavish dinner for a celebration in the middle of the week - such time is more related to "Everyday life and rest."&lt;/p&gt;

&lt;p&gt;In general, the body maintenance category can be omitted from time tracking altogether, especially at the beginning, or if you have other monitoring sources (smartwatch/bracelet).&lt;/p&gt;

&lt;p&gt;But we'll come back to this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Everyday life and rest
&lt;/h3&gt;

&lt;p&gt;Everyday life and rest are all activities related to maintaining the home, paperwork, taking/picking up a child from kindergarten, and other routines.&lt;/p&gt;

&lt;p&gt;Perhaps this category will be the first candidate for finding "time wasted." Well, or at least you'll have something to grab onto and start asking yourself questions like "what was I doing here for SIX hours? What kind of everyday life is that?"&lt;/p&gt;

&lt;p&gt;We should strive for a system where the everyday life and rest category is not used to plug holes in your schedule, except for emergencies like hospital visits, or household chores that cannot be rescheduled from the middle of the work schedule to another time.&lt;/p&gt;

&lt;p&gt;It's better not to mix productive and investment time with other activities because, according to Kahneman, the brain takes quite a long time to get into slow thinking - 20 minutes! This means if you work on a difficult intellectual task using the pomodoro method, you'll just be &lt;em&gt;warming up&lt;/em&gt; during the first pomodoro! It's not very productive to jump up and go on errands after that.&lt;/p&gt;

&lt;p&gt;You can start by tracking in this category all the time that doesn't fall under &lt;strong&gt;all other&lt;/strong&gt; categories.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Be vigilant, "all other" means including the one below!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Time wasted
&lt;/h3&gt;

&lt;p&gt;This category is for time that you "invested" in nonsense that brings you nothing (or almost nothing) useful in the long run.&lt;/p&gt;

&lt;p&gt;By default, it's not assumed to deliberately spend time (for hours) on nonsense. This category is rather designed for rewriting blocks of time (in whole or in part) from productive or investment time, during which we worked inefficiently or were distracted.&lt;/p&gt;

&lt;p&gt;Of course, it's also worth considering a scenario where you're in a position of personal growth, where you're already aware that you're wasting time on nonsense, but this nonsense is so pleasant to you and allows you to &lt;em&gt;seemingly&lt;/em&gt; rest well that you can't get rid of it yet. This is normal! No judgment!&lt;/p&gt;

&lt;p&gt;In this case, you can purposefully spend time on nonsense, so that later in the context of the week you can see how much goes there. Perhaps such a metric will help you stop doing this nonsense.&lt;/p&gt;

&lt;p&gt;In general, it's entirely your subjective matter to determine what is nonsense for you and what is everyday life/routine/rest. The topic of quality rest is a candidate for a separate article, &lt;a href="https://links.ivanzakutnii.com" rel="noopener noreferrer"&gt;subscribe&lt;/a&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical implementation of the system
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How to use this and what to do with it?
&lt;/h3&gt;

&lt;p&gt;The categories presented above are a starting point, a framework on which I suggest you build your own approach to measuring your real time expenditures.&lt;/p&gt;

&lt;p&gt;Here is a key point about composure and mindfulness.&lt;/p&gt;

&lt;p&gt;Measuring metrics by itself gives almost nothing; you need to have a goal, and after collection, analyze these metrics.&lt;/p&gt;

&lt;p&gt;The goal can be formulated very simply, for example - &lt;em&gt;"I want to manage to do more!"&lt;/em&gt;, or more specifically &lt;em&gt;"I want to stop spending so much time on nonsense"&lt;/em&gt;, or even &lt;em&gt;"I want to find time to start doing X"&lt;/em&gt;, where X can be anything - learning a new profession, or pursuing a hobby.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tools for collecting time metrics
&lt;/h3&gt;

&lt;p&gt;Here, for example, is what a report looks like in my chosen time tracker for the week:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F8vfa6abna5a8ylutmi8n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F8vfa6abna5a8ylutmi8n.png" alt=" " width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;32 hours on Saturday is certainly a lot!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I definitely remind you again - you don't have to be a time tracking maniac if you don't like it / don't need it / find it difficult. Look, I even have some errors here, that's normal!&lt;/p&gt;

&lt;p&gt;Maniacal tracking by too detailed categories exhausted me literally in a couple of days; eventually, I started using general categories, even for rest.&lt;/p&gt;

&lt;p&gt;It's important to say again about composure, about mindfulness - to the extent of understanding, be honest with yourself when tracking Rest / Everyday life. You must ruthlessly catch yourself trying to infiltrate obvious nonsense into any category other than the "nonsense category"!&lt;/p&gt;

&lt;p&gt;If it's difficult for you here too, resort to studying metrics, for example, screen time, which, as far as I know, all modern smartphones collect in a convenient format.&lt;/p&gt;

&lt;p&gt;If you see in the metrics that you spend a lot of time on nonsense - this is very important, that's exactly what we're looking for here! We're interested in how things are in reality, remember? So record everything honestly!&lt;/p&gt;

&lt;p&gt;On my graph, it's clear that Investment Time is more than Productive. Does this mean that I worked on tasks for 7 hours a week? No, investment time consists almost entirely of research tasks.&lt;/p&gt;

&lt;p&gt;And of course, I couldn't have tracked all this if the process wasn't convenient.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Not an advertisement at all - I use free &lt;code&gt;toggl&lt;/code&gt;. There are web, desktop, and mobile clients that synchronize normally, and as you can see in the screen above - you can build a pretty convenient categorization by "projects".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But why so little Wasted Time? Why do I need to track time if I'm already a productivity cyborg? No! Not a cyborg!&lt;/p&gt;

&lt;p&gt;As I've already said - before I started collecting these metrics, I had no time for any projects... Time tracking didn't help me spend less time on nonsense; time tracking helped me understand that I have problems with focus! And with focus not in terms of distraction to social networks, but with overloading myself with too many parallel tasks! We'll talk about this further.&lt;/p&gt;

&lt;p&gt;But for now, let's get back to tracking.&lt;/p&gt;

&lt;p&gt;The good news is that you already have everything to start tracking time. You definitely have a clock somewhere, even if you have a prehistoric button phone in your pocket.&lt;/p&gt;

&lt;p&gt;If you're an active gadget user, even better! You can choose from dozens, if not hundreds (if not thousands!) of applications for time tracking, with pomodoro, todo lists, and the like.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;But don't rush! Maybe you already have too many such applications; more on this later.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Just choose and start! Follow only the basic provisions of the framework and adapt it to yourself.&lt;/p&gt;

&lt;p&gt;Let's outline an action plan:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set a goal for yourself, give yourself a clear "direction" - why are you doing this at all.&lt;/li&gt;
&lt;li&gt;Figure out convenient category names for you, as long as they follow the general idea of division described above.&lt;/li&gt;
&lt;li&gt;Choose a way to start collecting metrics - an application on your phone, a notebook for handwriting, anything!&lt;/li&gt;
&lt;li&gt;Collect metrics for at least 7 days in a row&lt;/li&gt;
&lt;li&gt;Analyze the result&lt;/li&gt;
&lt;li&gt;Find oddities, inconveniences, improve categories if necessary, and repeat the collection of metrics.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of one week, you can collect metrics for two, or a whole month. If you suddenly find it simple and natural to start tracking time constantly in one format or another - don't be scared, you're not a maniac! Go ahead!&lt;/p&gt;

&lt;p&gt;In any case, once you find a system that's comfortable for you, it will be very useful to conduct such a weekly audit of your productivity at least once a quarter, or once every six months.&lt;/p&gt;

&lt;p&gt;It's also possible that after two such measurements, you'll feel that you never need them again, you've understood everything, and you've "ramped up" into effective and mindful work throughout life. Great, if you don't want to measure anymore - don't measure!&lt;/p&gt;

&lt;p&gt;Just don't forget about such a wonderful mechanism; perhaps it will come in handy when you start working on a new project, or get a new job, and suddenly find yourself overly tired and buried under a mountain of endless tasks, which are all urgent and needed to be done "yesterday."&lt;/p&gt;

&lt;h3&gt;
  
  
  The temptation to fall into specifications
&lt;/h3&gt;

&lt;p&gt;At some point, an excellent thought may visit you - why don't I start monitoring my time in more detail?&lt;/p&gt;

&lt;p&gt;For example - here I seem to be staying in the recommended categories, but I'll record how much time I spent working on project A, how much on calls for project B, how much time I ran on the treadmill, how much I worked with weight, how much time I spent preparing a salad, and how much I cooked borsch.&lt;/p&gt;

&lt;p&gt;Although sometimes such detail can help - for example, I found that sometimes I got stuck in the shower for 20 minutes - there's still a strong risk of getting confused, falling into excessive details, overcomplicating the process.&lt;/p&gt;

&lt;p&gt;I suggest sticking to the most generalized categories.&lt;/p&gt;

&lt;p&gt;Understand, you don't need to know how much total time per week you spend preparing salads. Your main task is to find out as accurately as possible how much time you &lt;em&gt;actually work&lt;/em&gt; and how much time you &lt;em&gt;actually spend on nonsense&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;From these metrics, you can delve into questions like "why so many tasks? Are they all really urgent and relevant?", or "why so many calls? Is it true that they're all productive...?", and so on.&lt;/p&gt;

&lt;p&gt;You can always go into any detail, as long as the metrics are collected at all. And it's by no means certain (rather the opposite) that it will actually benefit you to track time separately for each work task. That's madness.&lt;/p&gt;




&lt;p&gt;Depending on your main activity and lifestyle, the proposed categories may seem too general or not quite correct.&lt;/p&gt;

&lt;p&gt;It's perfectly normal to &lt;em&gt;slightly&lt;/em&gt; specify or rename them. For example, your categories might look like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writing blog articles (investment time)&lt;/li&gt;
&lt;li&gt;Writing code / solving work routine (productive time)&lt;/li&gt;
&lt;li&gt;Learning (investment time)&lt;/li&gt;
&lt;li&gt;Exercising (everyday life and rest)&lt;/li&gt;
&lt;li&gt;Reading books / articles / blogs (everyday life and rest)&lt;/li&gt;
&lt;li&gt;Doing nonsense (Nonsense!)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Working on client projects (productive time)&lt;/li&gt;
&lt;li&gt;Studying new trends and design tools (investment time)&lt;/li&gt;
&lt;li&gt;Creating works for portfolio (investment time)&lt;/li&gt;
&lt;li&gt;Household chores and personal life (everyday life and rest)&lt;/li&gt;
&lt;li&gt;Aimless scrolling of social networks (time wasted)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that in both of these examples, I completely omitted tracking body maintenance, and that's perfectly normal.&lt;/p&gt;




&lt;p&gt;If you have a fitness bracelet or some kind of smartwatch that tracks how much you sleep, then consider that you already have a metric about sleep.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pay attention to the quality of your sleep and rest! If you don't sleep, then no tracking will save you!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Meals and their preparation may naturally be visible in the calendar or between records of time spent, so they can also be omitted (just make sure that lunch doesn't turn into scrolling news/TikTok for another 40 minutes on top.)&lt;/p&gt;

&lt;p&gt;As you can see - there's plenty of space!&lt;/p&gt;

&lt;p&gt;The main thing is that your categories follow the division into productive and investment time, healthy routine and everyday life, and finally a category for nonsense.&lt;/p&gt;

&lt;p&gt;The Nonsense category is needed even if you're super successful and mindful and don't spend more than 30 minutes a week on nonsense. Just by stopping to even consider that such a category exists, you're already giving nonsense more chances to seep into your life.&lt;/p&gt;

&lt;h2&gt;
  
  
  From collecting metrics to action
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Analysis of the data obtained
&lt;/h3&gt;

&lt;p&gt;Analyzing the collected metrics is a slightly more complex process.&lt;/p&gt;

&lt;p&gt;When you start measuring time, you'll have the opportunity to study the proportions of your time investments across days or, even more effectively, an entire week.&lt;/p&gt;

&lt;p&gt;However, the numbers themselves won't give much — in parallel, it's necessary to develop the ability to work qualitatively and rationally &lt;em&gt;directly at the moment of performing tasks.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This means increasing the level of composure and efficiency in each work session, which, combined with the analysis of time metrics, will give results.&lt;/p&gt;

&lt;p&gt;This is exactly the reason why I included mentions of TODO lists and pomodoro practice in this article in the Primary Metrics section (and other places).&lt;/p&gt;

&lt;p&gt;If you don't start developing your composure in the moment, you'll have nothing to record in wasted time.&lt;/p&gt;

&lt;p&gt;I'd like to tell you that you can neglect composure, but unfortunately, that's not the case. You need some additional mechanism that will help you determine what time was wasted and what wasn't. And the main mechanism here is your responsible attentiveness to your own life activity.&lt;/p&gt;

&lt;p&gt;Let's look at a couple of examples of how to determine time wasted in vain. It sounds simpler than it might seem.&lt;/p&gt;

&lt;p&gt;For example, you have a scheduled zoom call with colleagues. If the call was productive, in the sense that 2/3 wasn't small talk, and after the end of the call, it became clear what to do next. This understanding is expressed purely in the subjective feeling of &lt;em&gt;"we agreed about X, now I need to do A and B"&lt;/em&gt;, against which there is simultaneously a rather bright and terribly vague feeling of &lt;em&gt;"What were we talking about? Why did we gather? What should I do next, huh?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you catch yourself on option 2 - boldly record the time of such calls as wasted.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Right now, you probably don't have almost any mechanisms to influence the regularity and quality of such calls, especially if you're an individual contributor, not an operational manager. A large number of such "vague" calls is a serious indicator of problems with processes and probably overall productivity in the organization, so I can only suggest that you focus on areas where you can manage your time, and subscribe to me, as I will definitely continue to write about productivity and systems thinking.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Another example. Let's say you've decided that, like me, you're comfortable working on tasks using the pomodoro method to fight distractions - we turn on the timer and for 25 minutes, no TikTok!&lt;/p&gt;

&lt;p&gt;If you really didn't get distracted - excellent! The pomodoro goes into productive or investment time depending on the work you were doing. Now you can space out for five minutes, or &lt;em&gt;rest well&lt;/em&gt; :)&lt;/p&gt;

&lt;p&gt;If you did get distracted, and significantly so, for example, you read emails for five minutes or more - this is definitely time wasted. I prefer to send all 25 minutes to the "trash," which is both cruel and simpler than cutting out 5-8 minutes from a pomodoro. Besides - then during analysis, these blocks will attract your attention and you'll be able to reflect better.&lt;/p&gt;

&lt;h3&gt;
  
  
  From metrics to action: task management
&lt;/h3&gt;

&lt;p&gt;Now that we've figured out how to collect time metrics, the logical question is what to do with the freed-up time and how to organize your tasks.&lt;/p&gt;

&lt;p&gt;After all, the ultimate goal of collecting metrics is not just to know where time goes, but to learn how to use it more effectively.&lt;/p&gt;

&lt;p&gt;And here lurks a terrible devil.&lt;/p&gt;

&lt;p&gt;Have you ever caught yourself having 500 interesting articles you want to read, and 48 projects you really want to start doing? While 10 of them are &lt;em&gt;already in progress.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Please remember my story, which I wrote at the beginning of the article - in the previous year, I almost didn't waste time on nonsense, I worked a lot. But I didn't become more productive. Therefore, in addition to collecting metrics, you also need to learn &lt;em&gt;focus&lt;/em&gt;, concentration on what's important, concrete matter.&lt;/p&gt;

&lt;p&gt;There are also a bunch of methods for this, but I'll suggest you simply think and show rationalism. When you start collecting metrics and figure out where you're spending time, this will be especially useful if you suddenly find yourself in the same situation I was in - buried under a pile of tasks, multiple projects, and so on.&lt;/p&gt;

&lt;p&gt;First - throw away all the tools you're already using, keep only one. Paper planner, todo application, obsidian with some plugin where there's a calendar and daily tasks - choose something that will be convenient for you or at least seem convenient (you can always change).&lt;/p&gt;

&lt;p&gt;But don't go crazy; for tasks, you need one tool.&lt;br&gt;
Or at worst - one for work, another for all personal projects for purely logical separation.&lt;/p&gt;

&lt;p&gt;If you've been using multiple applications, then looking a little closer, it will become clear what you &lt;em&gt;really used&lt;/em&gt; - in one of them there will be the most tasks, notes, visible activity. Great - that's it. It's &lt;em&gt;already convenient&lt;/em&gt; for you.&lt;/p&gt;

&lt;p&gt;Now clean your planner of garbage that steals your attention and time.&lt;/p&gt;

&lt;p&gt;Perhaps, like me, you started at some point to use the todo list as a repository for notes, reading lists, and so on.&lt;/p&gt;

&lt;p&gt;REMOVE EVERYTHING from your TODO list that doesn't relate to tasks you're currently working on - work and your projects, it doesn't matter. Leave only what you can't remove, something that's already somehow in progress, or is about to be taken into work and can't be canceled anymore.&lt;/p&gt;

&lt;p&gt;There are two possible scenarios here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You threw out all the garbage and everything became crystal clear; it turns out everything isn't so bad, and you only have 1-2 projects in progress, and you were only wasting time on nonsense&lt;/li&gt;
&lt;li&gt;And more likely - Everything is terrible, you have 4-5 or more projects simultaneously, and a bunch of small tasks that logically don't relate to any projects but take away a lot of your resources.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So, for those who found themselves in the second scenario - consider each of your tasks, and ask yourself a specific question:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;What is this task, to which &lt;em&gt;project does it belong?&lt;/em&gt; Is it clear at all what it is and when it needs to be done? If not - then you forgot to throw away some reminder or note that has no place in a TODO list. Do it!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If it's still a clear task, then ask yourself what its essence is, what benefit and to whom will it bring - what will it affect in the real world? How well is it worked out? Does it really need to be dealt with &lt;em&gt;right now&lt;/em&gt;?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Congratulations, you've either identified a valuable task, or found garbage again. There's still a chance that you're looking at &lt;em&gt;something&lt;/em&gt; potentially useful - send it to the archive or some watch later on YouTube. The main thing is to clear up the planner.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Focus – that very composure
&lt;/h3&gt;

&lt;p&gt;Okay, we've got a clearer understanding of where our time goes and how to organize tasks. But there's one more critically important element, a universal tool of productivity in any business - the ability to focus.&lt;/p&gt;

&lt;p&gt;Without this skill, even the most accurate metrics and perfectly organized task lists won't bring the desired result.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I have good news for you! If you've read to this point without scrolling and in one sitting - you probably have good focus! The main thing is to clear out the garbage and collect metrics :)&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Multitasking is a myth. It's not the path to success; it's the path to nowhere.&lt;/p&gt;

&lt;p&gt;You need to learn to concentrate ON ONE thing, at every moment of time and on any scale.&lt;/p&gt;

&lt;p&gt;Note that throughout this article, I've repeatedly mentioned the word composure. Your quality and composed attention is insanely important always, at any stage when we start to live purposefully, not just somehow.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Without composure, everything will always be "just somehow"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This may sound too stupid, but the longer you concentrate on one thing, the more results you'll achieve.&lt;/p&gt;

&lt;p&gt;I'm not suggesting you necessarily concentrate on one thing for your entire life, but that's also an option. Imagine if you choose the meaning of your life - knitting scarves and hats. Every day you learn to knit, becoming better and better.&lt;/p&gt;

&lt;p&gt;But that doesn't mean you'll only knit.&lt;/p&gt;

&lt;p&gt;For example, at some point, you might want to knit for family and friends, and then it comes to mind to start earning from this - take it for sale.&lt;/p&gt;

&lt;p&gt;And here you seem to have one goal and one business, but now "side quests" appear like:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Learn marketing&lt;/li&gt;
&lt;li&gt;Develop a personal brand&lt;/li&gt;
&lt;li&gt;Set up social media&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And so on, but globally - the goal is one.&lt;/p&gt;

&lt;p&gt;Many people for this reason jump from one thing to another, can't find themselves. Because they didn't set any of their undertakings as a goal, didn't plan to thoroughly try and understand whether they need it or not, but started doing something in an emotional impulse inspired by others' opinions.&lt;/p&gt;

&lt;p&gt;The point is that concrete goal setting is also a skill from the category of composure.&lt;/p&gt;

&lt;p&gt;And nevertheless, if you've chosen something more complex than knitting, for example, an engineering career - there can be many sub-goals that you can spend your whole life on.&lt;/p&gt;

&lt;p&gt;That's why it's especially important to focus on one thing.&lt;/p&gt;

&lt;p&gt;One brain definitely won't be enough for this, in the sense of the brain itself. That's why I'm telling this whole story about metrics - we need to constantly be reinforced by breadcrumbs &lt;em&gt;from reality&lt;/em&gt;, so as not to forget what and why we're doing, where and why we're going, and who we are in general.&lt;/p&gt;

&lt;p&gt;If you don't know at all what you need, regardless of how old you are - just start consciously choosing things and trying to do them, until you get bored, until you start understanding that you don't get satisfaction from what you're doing.&lt;/p&gt;

&lt;p&gt;Just try, the main thing is to start doing at least something!&lt;/p&gt;

&lt;p&gt;The secret is that by focusing on something for a week-two-month, you'll still learn something useful - it won't be wasted. At the very least, you can learn composure! :)&lt;/p&gt;

&lt;p&gt;The main thing - stop wasting your life on complete nonsense and terrible dispersion!&lt;/p&gt;

&lt;p&gt;Okay, it's time to finish this lyrical digression.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why it's so hard for us to concentrate
&lt;/h3&gt;

&lt;p&gt;The modern world creates unprecedented conditions in which it's increasingly difficult for our brain to maintain concentration.&lt;/p&gt;

&lt;p&gt;First, we've evolved so that we can effectively process a limited amount of information. But modern technologies create a data flow that causes terrible cognitive overload, making it difficult to process and remember important information.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;That's why your attention is truly invaluable resource, second in value only to time. And it should also be treated precisely as a resource, for a very simple reason - attention and time are very closely linked!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Second, multitasking is a disgusting myth. The brain cannot process multiple tasks simultaneously. Instead, it quickly switches between them, which requires significant energy expenditure and reduces work efficiency. With each such switch, we not only sag in productivity but also increase the probability of errors.&lt;/p&gt;

&lt;p&gt;Third, the problem with dopamine. Notifications and social networks exploit the brain's dopamine reward system; this is also no secret. These things are &lt;em&gt;literally&lt;/em&gt; created in such a way as to rob you - &lt;em&gt;steal your attention!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Each new notification causes a small surge of dopamine, which creates an addiction to constant small "rewards" and disaccustoms us from the long concentration necessary for deep work. The endless feed is generally a complete mess about which I don't even want to talk.&lt;/p&gt;

&lt;p&gt;The constant flow of information leads to brain burnout. Symptoms include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deficit of composure/attention - very difficult to concentrate on one task&lt;/li&gt;
&lt;li&gt;Decision-making paralysis (even simple ones)&lt;/li&gt;
&lt;li&gt;Dependence on technology to perform &lt;em&gt;basic&lt;/em&gt; cognitive tasks - for example, a person doesn't even try to add 17+8 in their mind and immediately reaches for a phone calculator&lt;/li&gt;
&lt;li&gt;Chronic stress and anxiety&lt;/li&gt;
&lt;li&gt;Sleep disturbance - seems like you didn't drink coffee, and you're physically tired as a horse, but it's very hard to fall asleep&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Recognizing all these signals and problems is the first step to restoring control over your attention (and life!)&lt;/p&gt;

&lt;p&gt;The good news is that time tracking and developing awareness about where your concentration is really going can help with this trouble.&lt;/p&gt;

&lt;p&gt;There are other useful practices that can help with attention training, but the article is already too long, so I'll tell about them another time.&lt;/p&gt;

&lt;p&gt;And besides - I sincerely believe that grounding in metrics, which we're considering today, can give enough impetus for you to start solving this problem yourself (if it exists). Awareness, knowledge about the catastrophe won't let you close your eyes further.&lt;/p&gt;

&lt;p&gt;I hope you haven't chickened out.&lt;/p&gt;

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

&lt;p&gt;We've examined three keys to real productivity:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Collecting primary time metrics through a simple categorization system;&lt;/li&gt;
&lt;li&gt;Effectively organizing tasks in a single space;&lt;/li&gt;
&lt;li&gt;Developing the ability to focus on one thing until it's completed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Collect metrics! I believe you can do it!&lt;/p&gt;

&lt;p&gt;This article turned out very large, and perhaps a bit chaotic in places, because the topic is simultaneously very important and very extensive. And too... personal! Because I experienced a qualitative leap in productivity when I discovered my specific problems - task overload.&lt;/p&gt;

&lt;p&gt;Without time tracking, I wouldn't have seen what was already in the most visible place. So if I could force you to take away at least something from this article, at least one thought, I would force you to remember not about time categorization, but about grounding in reality.&lt;/p&gt;

&lt;p&gt;Please, don't live exclusively in your head!&lt;/p&gt;

&lt;p&gt;Subscribe to my social networks; I write about systems engineering, neural networks (human and non-living), software engineering: &lt;a href="https://links.ivanzakutnii.com" rel="noopener noreferrer"&gt;https://links.ivanzakutnii.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
    </item>
    <item>
      <title>Embedding Design Into Code</title>
      <dc:creator>Ivan Zakutnii</dc:creator>
      <pubDate>Wed, 16 Oct 2024 07:45:41 +0000</pubDate>
      <link>https://forem.com/m0n0x41d/embedding-design-into-code-n4e</link>
      <guid>https://forem.com/m0n0x41d/embedding-design-into-code-n4e</guid>
      <description>&lt;p&gt;Over-engineering can probably happen at almost any stage of design (or lack of design) and actual software development.&lt;/p&gt;

&lt;p&gt;For example, when we’re implementing an entity and its methods, it’s far from guaranteed that all these methods will be small and elegant. And what’s more — they don’t always have to be.&lt;/p&gt;

&lt;p&gt;Yet, when I see a function or method of 30-50 lines on my editor screen, I automatically ask myself: what can I factor out here?&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Problems with Blind Faith in SRP&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In a method of this size, there are always smaller logical pieces (SRP), and they are always easy to spot, especially if the code was just written and we’re still in the context.&lt;/p&gt;

&lt;p&gt;But should we always start breaking the method right away? Will we always benefit from this?&lt;/p&gt;

&lt;p&gt;Well, if you manage to do such factoring quickly and elegantly, then yes, you’re definitely drawing logical boundaries in your code, but you’re also adding a certain level of redirection.&lt;/p&gt;

&lt;p&gt;Blindly following SRP everywhere can lead to a lot of methods.&lt;/p&gt;

&lt;p&gt;Even if we try to arrange them in some &lt;em&gt;“logical”&lt;/em&gt; and readable order, in the future, we (or other developers — pretty much the same thing) will likely have to scroll the code editor back and forth to figure out and remember what our methods do and when, following all the added redirection.&lt;/p&gt;

&lt;p&gt;Of course, if we don’t do this factoring, we naturally end up in the opposite situation — keeping all our code in one method, we don’t get clearly defined logical boundaries but avoid an explosion of methods and nested calls.&lt;/p&gt;

&lt;p&gt;To be more precise — we don’t get &lt;em&gt;as clearly defined&lt;/em&gt; boundaries that SRP creates when logic is localized. This does not mean that we can’t express them.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Embedded Design&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;I once wrote that note called &lt;a href="https://ivanzakutnii.com/blog/Bullshit-of-self-documenting-code" rel="noopener noreferrer"&gt;"Bullshit Of Self-Documenting Code"&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Sure, when all our methods and arguments are properly named, and we even follow popular style guides, the code becomes much more readable and better than some randomly obfuscated code full of single-letter identifiers and one-liners.&lt;/p&gt;

&lt;p&gt;And yet, by using comments or language syntax features, we can mark logical boundaries in a method without splitting it according to SRP.&lt;/p&gt;

&lt;p&gt;It’s simple, and it’s pretty cool.&lt;br&gt;
Especially in C-like languages with curly braces, where braces help group code within a function.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First, it’s regularly much clearer than any “self-documenting” tricks, and allow you to express more formality.&lt;/li&gt;
&lt;li&gt;Second, almost all IDEs allow you to collapse such blocks of code marked by braces.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On top of that, these blocks can (and likely should) be marked with informative and expressive comments — awesome!&lt;/p&gt;

&lt;p&gt;Using such an &lt;em&gt;embedded design&lt;/em&gt;, we not only avoid blurring the logic across dozens of methods, but we also maintain a higher level of abstraction in our code, making it &lt;em&gt;observable&lt;/em&gt;, as pieces of code alone do not reflect the design well.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We are literally embedding concepts from the “Programming in the Large” level into “Programming in the Small” world. These are cognitive shortcuts you’ll thank yourself for later.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Moreover, by doing this work in advance, we prepare everything for &lt;em&gt;“physical”&lt;/em&gt; factoring. If we ever need it, doing so will be super easy because everything is already done, and we don’t need to think about how to do it!&lt;/p&gt;

&lt;p&gt;Of course, embedded design shouldn’t be used as an excuse for boxing everything into one class and its methods. That’s criminal negligence, not embedded design.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Applicability in Non-C-like Languages&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;What should we do with Python? There are no curly braces here, and if you suggest to your colleagues that team should start writing in &lt;a href="https://github.com/mathialo/bython" rel="noopener noreferrer"&gt;Bython&lt;/a&gt;, they might call &lt;em&gt;911&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;At first glance It seems we don’t have many options for applying embedded design in Python projects. We can either write clear comments for code blocks or declare logically related blocks as separate functions within a method and call them in the desired order at the end of the method. Okay.&lt;/p&gt;

&lt;p&gt;I’d like to say the first option is better, but the second isn’t bad either. It’s probably just a matter of style and personal preference.&lt;/p&gt;

&lt;p&gt;Python is still a programming language, and even though it "lacks" curly braces, we’re still writing the program text in &lt;em&gt;files&lt;/em&gt;, and in most cases, we’re writing OOP code. We can fully follow the idea of embedded design.&lt;/p&gt;

&lt;p&gt;It can be applied not only within the scope of a specific method but also at the level of organizing functions in files or methods in a class.&lt;/p&gt;

&lt;p&gt;The essence is the same — we divide functions or methods into logical clusters, and those clusters are separated by expressive but &lt;em&gt;informative&lt;/em&gt; comments.&lt;/p&gt;

&lt;p&gt;However, at this level, it seems that not all such logical subcategories can always adequately reflect designed business processes.&lt;/p&gt;

&lt;p&gt;Nevertheless, we still get the same advantages upfront — our classes or files, following the idea of embedded design, are not only cognitively friendly for our future selves or new colleagues, but they are also ready for further factoring, and may be even re-factoring transformations.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Obviously, both approaches — SRP-based factoring and embedded design — have their pros and cons. But whats more important - these approaches are &lt;em&gt;qualitatively different&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Of course, they’re not fully mutually exclusive, and choosing when to use each one depends entirely on the context (both - large and small) and your preferences.&lt;/p&gt;

&lt;p&gt;Hopefully, you prefer cognitive efficiency over mental suffering.&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>design</category>
    </item>
    <item>
      <title>The Wisdom of Avoiding Conditional Statements</title>
      <dc:creator>Ivan Zakutnii</dc:creator>
      <pubDate>Tue, 13 Aug 2024 15:22:54 +0000</pubDate>
      <link>https://forem.com/m0n0x41d/the-wisdom-of-avoiding-conditional-statements-22a7</link>
      <guid>https://forem.com/m0n0x41d/the-wisdom-of-avoiding-conditional-statements-22a7</guid>
      <description>&lt;p&gt;Cyclomatic complexity is a metric that measures the complexity and tangledness of code.&lt;/p&gt;

&lt;p&gt;High cyclomatic complexity is not a good thing, quite the opposite.&lt;/p&gt;

&lt;p&gt;Simply put, cyclomatic complexity is directly proportional to the number of possible execution paths in a program. In other words, cyclomatic complexity and the total number of conditional statements (especially their nesting) are closely related.&lt;/p&gt;

&lt;p&gt;So today, let’s talk about conditional statements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Anti-if
&lt;/h3&gt;

&lt;p&gt;In 2007, &lt;strong&gt;Francesco Cirillo&lt;/strong&gt; launched a movement called &lt;a href="https://www.antiifprogramming.com/about-the-anti-if.php" rel="noopener noreferrer"&gt;Anti-if&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Francesco Cirillo&lt;/strong&gt; is the guy who came up with the Pomodoro technique. I'm writing this blog post right now “under the Pomodoro.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I guess we all quickly figured out what this campaign is about from its name. Interestingly, the movement has quite a few computer scientists among its followers.&lt;/p&gt;

&lt;p&gt;Their arguments are rock solid — &lt;code&gt;if&lt;/code&gt; statements are evil, leading to exponential growth in program execution paths.&lt;/p&gt;

&lt;p&gt;In short, that’s cyclomatic complexity. The higher it is, the harder it is not only to read and understand the code but also to cover it with tests.&lt;/p&gt;

&lt;p&gt;Sure, we have kind of an "opposite" metric — &lt;code&gt;code coverage&lt;/code&gt;, which shows how much of your code is covered by tests. But does this metric, along with the rich tools in our programming languages for checking coverage, justify ignoring cyclomatic complexity and sprinkling &lt;code&gt;if&lt;/code&gt; statements around just based on "instinct"?&lt;/p&gt;

&lt;p&gt;I think not.&lt;/p&gt;




&lt;p&gt;Almost every time I catch myself about to nest one &lt;code&gt;if&lt;/code&gt; inside another, I realize that I'm doing something really silly that could be rewritten differently — either without nested &lt;code&gt;if&lt;/code&gt;'s or without &lt;code&gt;if&lt;/code&gt;'s at all.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You did notice the word "almost," right?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I didn't start noticing this right away. If you look at my GitHub, you'll find more than one example of old code with not just high cyclomatic complexity but straight-up cyclomatic madness.&lt;/p&gt;

&lt;p&gt;What helped me become more aware of this issue? Probably experience and a few smart things I learned and embraced about a year ago. That's what I want to share with you today.&lt;/p&gt;




&lt;h3&gt;
  
  
  Two Sacred Techniques for the Destruction of &lt;code&gt;if&lt;/code&gt; Statements
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Padawan, move each conditional check of an unknown value to a place where that value is already known.&lt;/li&gt;
&lt;li&gt;Padawan, change your mental model of the encoded logic so that it no longer requires conditional checks.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  1. Make Unknown Known
&lt;/h4&gt;

&lt;p&gt;Checking something when we don't "know" it yet is probably the most common source of using conditional statements based on "instinct."&lt;/p&gt;

&lt;p&gt;For example, suppose we need to do something based on a user's age, and we must ensure the age is valid (falls within reasonable ranges). We might end up with code like this:&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;Optional&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_age&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&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;age&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Age cannot be null&lt;/span&gt;&lt;span class="sh"&gt;"&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;age&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="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Age must be between 0 and 150&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We've all seen and probably written similar code hundreds of times.&lt;/p&gt;

&lt;p&gt;How do we eliminate these conditional checks by following the discussed &lt;em&gt;meta-principle&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;In our specific case with age, we can apply my favorite approach — moving away from primitive obsession towards using a custom data type.&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;Age&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;__init__&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;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&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;value&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="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Age must be between 0 and 150&lt;/span&gt;&lt;span class="sh"&gt;"&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;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_value&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;int&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_age&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Age&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Age is guaranteed to be valid, process it directly
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hooray, one less &lt;code&gt;if&lt;/code&gt;! The validation and verification of the age are now always "where the age is known" — within the responsibility and scope of a separate class.&lt;/p&gt;

&lt;p&gt;We can go further/differently if we want to remove the &lt;code&gt;if&lt;/code&gt; in the &lt;code&gt;Age&lt;/code&gt; class, perhaps by using a Pydantic model with a validator or even replacing &lt;code&gt;if&lt;/code&gt; with &lt;code&gt;assert&lt;/code&gt; — it doesn't matter now.&lt;/p&gt;




&lt;p&gt;Other techniques or mechanisms that help to get rid of conditional checks within this same meta-idea include approaches like replacing conditions with polymorphism (or anonymous lambda functions) and decomposing functions that have sneaky boolean flags.&lt;/p&gt;

&lt;p&gt;For example, this code (horrible boxing, right?):&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;PaymentProcessor&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;process_payment&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;payment_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&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;str&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;payment_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;credit_card&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="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;process_credit_card_payment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;payment_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;paypal&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="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;process_paypal_payment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;payment_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bank_transfer&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="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;process_bank_transfer_payment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Unknown payment type&lt;/span&gt;&lt;span class="sh"&gt;"&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;process_credit_card_payment&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;amount&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;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&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;Processed credit card payment of &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;amount&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_paypal_payment&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;amount&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;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&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;Processed PayPal payment of &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;amount&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_bank_transfer_payment&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;amount&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;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&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;Processed bank transfer payment of &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;amount&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;And it doesn't matter if you replace &lt;code&gt;if/elif&lt;/code&gt; with &lt;code&gt;match/case&lt;/code&gt; — it's the same garbage!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's quite easy to rewrite it 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="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;PaymentProcessor&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;process_payment&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;amount&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;str&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;class&lt;/span&gt; &lt;span class="nc"&gt;CreditCardPaymentProcessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PaymentProcessor&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;process_payment&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;amount&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;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&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;Processed credit card payment of &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;amount&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PayPalPaymentProcessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PaymentProcessor&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;process_payment&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;amount&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;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&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;Processed PayPal payment of &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;amount&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BankTransferPaymentProcessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PaymentProcessor&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;process_payment&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;amount&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;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&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;Processed bank transfer payment of &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;amount&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;right?&lt;/p&gt;




&lt;p&gt;The example of decomposing a function with a boolean flag into two separate functions is as old as time, painfully familiar, and incredibly annoying (in my honest opinion).&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;process_transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transaction_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                                &lt;span class="n"&gt;amount&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="n"&gt;is_internal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&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;is_internal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Process internal transaction
&lt;/span&gt;        &lt;span class="k"&gt;pass&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Process external transaction
&lt;/span&gt;        &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two functions will be much better in any case, even if 2/3 of the code in them is identical! This is one of those scenarios where a trade-off with DRY is the result of common sense, making the code just better.&lt;/p&gt;




&lt;p&gt;The big difference here is that mechanically, on autopilot, we are unlikely to use these approaches unless we've internalized and developed &lt;em&gt;the habit&lt;/em&gt; of thinking through the lens of this principle.&lt;/p&gt;

&lt;p&gt;Otherwise, we'll automatically fall into &lt;code&gt;if: if: elif: if...&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Free Your Mind, Neo
&lt;/h4&gt;

&lt;p&gt;In fact, the second technique is the only real one, and the earlier "first" technique is just preparatory practices, a shortcut for getting &lt;em&gt;in place&lt;/em&gt; :)&lt;/p&gt;

&lt;p&gt;Indeed, the only ultimate way, method — call it what you will — to achieve simpler code, reduce cyclomatic complexity, and cut down on conditional checks is &lt;em&gt;making a shift in the mental models we build in our minds to solve specific problems&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I promise, one last silly example for today.&lt;/p&gt;

&lt;p&gt;Consider that we're urgently writing a backend for some online store where user can make purchases without registration, or with it.&lt;/p&gt;

&lt;p&gt;Of course, the system has a &lt;code&gt;User&lt;/code&gt; class/entity, and finishing with something like this is easy:&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;process_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                  &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&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;user&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Process order for a registered user
&lt;/span&gt;       &lt;span class="k"&gt;pass&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Process order for a guest user
&lt;/span&gt;           &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But noticing this nonsense, thanks to the fact that our thinking has already shifted in the right direction (I believe), we'll go back to where the &lt;code&gt;User&lt;/code&gt; class is defined and rewrite part of the code in something like this:&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;User&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;__init__&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&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;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_order&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;order_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&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;class&lt;/span&gt; &lt;span class="nc"&gt;GuestUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&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;__init__&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="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Guest&lt;/span&gt;&lt;span class="sh"&gt;"&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;process_order&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;order_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&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;So, the essence and beauty of it all is that we don't clutter our minds with various patterns and coding techniques to eliminate conditional statements and so on.&lt;/p&gt;

&lt;p&gt;By shifting our focus to the meta-level, to a higher level of abstraction than just &lt;a href="https://ivanzakutnii.com/blog/Levels-of-reasoning-about-software" rel="noopener noreferrer"&gt;the level of reasoning about lines of code&lt;/a&gt;, and following the idea we've discussed today, the right way to eliminate conditional checks and, in general, more correct code will &lt;em&gt;naturally emerge&lt;/em&gt;.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;A lot of conditional checks in our code arise from the cursed None/Null leaking into our code, so it's worth mentioning the quite popular &lt;a href="https://en.wikipedia.org/wiki/Null_object_pattern" rel="noopener noreferrer"&gt;Null Object pattern&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Clinging to Words, Not Meaning
&lt;/h3&gt;

&lt;p&gt;When following Anti-if, you can go down the wrong path by clinging to words rather than meaning and blindly following the idea that "if is bad, if must be removed.”&lt;/p&gt;

&lt;p&gt;Since conditional statements are &lt;em&gt;semantic&lt;/em&gt; rather than &lt;em&gt;syntactic&lt;/em&gt; elements, there are countless ways to remove the &lt;code&gt;if&lt;/code&gt; token from your code &lt;em&gt;without changing the underlying logic&lt;/em&gt; in our beloved programming languages.&lt;/p&gt;

&lt;p&gt;Replacing an &lt;code&gt;elif&lt;/code&gt; chain in Python with a &lt;code&gt;match/case&lt;/code&gt; isn’t what I’m talking about here.&lt;/p&gt;

&lt;p&gt;Logical conditions stem from the mental “model” of the system, and there’s no &lt;em&gt;universal&lt;/em&gt; way to "just remove" conditionals entirely.&lt;/p&gt;

&lt;p&gt;In other words, cyclomatic complexity and overall code complexity aren’t tied to the physical representation of the code — the letters and symbols written in a file.&lt;/p&gt;

&lt;p&gt;The complexity comes from the &lt;em&gt;formal expression&lt;/em&gt;, the &lt;em&gt;verbal or textual explanation&lt;/em&gt; of why and how specific code works.&lt;/p&gt;

&lt;p&gt;So if we change something in the code, and there are fewer if statements or none at all, but the verbal explanation of same code remains the same, &lt;em&gt;all we’ve done is change the representation of the code&lt;/em&gt;, and the change itself doesn’t really mean anything or make any improvement.&lt;/p&gt;

</description>
      <category>softwaredesign</category>
      <category>softwaredevelopment</category>
      <category>python</category>
    </item>
    <item>
      <title>What the heck is homomorphism?</title>
      <dc:creator>Ivan Zakutnii</dc:creator>
      <pubDate>Sun, 19 May 2024 19:15:54 +0000</pubDate>
      <link>https://forem.com/m0n0x41d/what-the-heck-is-homomorphism-ilc</link>
      <guid>https://forem.com/m0n0x41d/what-the-heck-is-homomorphism-ilc</guid>
      <description>&lt;p&gt;Hello. Continuing my dive into the nature of abstractions in software engineering, I've reached yet another rabbit hole.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Yes, rabbit holes turn out to be recursive in nature.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, &lt;a href="https://ivanzakutnii.com/blog/What-is-Abstraction" rel="noopener noreferrer"&gt;last time&lt;/a&gt; we found out that the most accurate definition of abstraction seems to be the words of Edsger Dijkstra:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Being abstract is something &lt;em&gt;profoundly&lt;/em&gt; different from being vague... The purpose of abstraction is not to be vague, but to create a &lt;em&gt;new semantic level&lt;/em&gt; in which one can be absolutely precise.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Thanks to &lt;a href="https://www.jameskoppel.com/" rel="noopener noreferrer"&gt;Jimmy Koppel&lt;/a&gt;, it turns out that Professor &lt;a href="https://en.wikipedia.org/wiki/Gerald_Jay_Sussman" rel="noopener noreferrer"&gt;Gerald Jay Sussman&lt;/a&gt; rightly considers abstraction too broad a "suitcase term" that denotes too many things. However, he sees two related definitions that apply to software engineering.&lt;/p&gt;

&lt;p&gt;Definition 1 - "giving names to things produced by the second definition";&lt;br&gt;
Definition 2 - Well, it is tough stuff, following from the &lt;a href="https://en.wikipedia.org/wiki/Fundamental_theorem_on_homomorphisms" rel="noopener noreferrer"&gt;Fundamental Theorem of Homomorphisms&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As it is tough, and I promised to do my best in writing as simply as possible... I will try, but sorry, I will most likely fail again :)&lt;/p&gt;

&lt;p&gt;But it is &lt;strong&gt;so&lt;/strong&gt; interesting!&lt;/p&gt;

&lt;h2&gt;
  
  
  Fundamental Theorem on Homomorphisms for Dummies
&lt;/h2&gt;

&lt;p&gt;Okay, take a look at this beautifully cryptic diagram:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fne0jfudc9dzs5kkzlv39.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fne0jfudc9dzs5kkzlv39.png" alt="fundamental-homomorphism" width="586" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's break it down.&lt;/p&gt;

&lt;p&gt;Imagine you have a collection of shapes: circles, squares, and triangles.&lt;/p&gt;

&lt;p&gt;Each shape can be either red, blue, or green.&lt;/p&gt;

&lt;p&gt;Let's define an operation called "change color" which, obviously, changes the color of any of your shapes to another one.&lt;/p&gt;

&lt;p&gt;So, &lt;code&gt;G&lt;/code&gt; represents all collections of our shapes of different colors.&lt;br&gt;
And &lt;code&gt;f&lt;/code&gt; is our "change color" operation.&lt;/p&gt;

&lt;p&gt;Potentially, we can have sets of shapes in &lt;code&gt;G&lt;/code&gt;, for which &lt;code&gt;f&lt;/code&gt; will do the &lt;em&gt;same transformation&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;For example, it can turn out that &lt;code&gt;f&lt;/code&gt; will change color to specific one based on shape form so we will have at least three groups:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Circles -&amp;gt; Change color to green;&lt;/li&gt;
&lt;li&gt;Squares -&amp;gt; Change color to blue;&lt;/li&gt;
&lt;li&gt;Triangles -&amp;gt; Change color to red.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;At least&lt;/em&gt;, because we can already have green circles and so on, so &lt;code&gt;f&lt;/code&gt; will not change anything, but let's not take that into account now.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Each of our groups we select into a list, and we obtain the so-called &lt;em&gt;kernel&lt;/em&gt; &lt;code&gt;ϕ&lt;/code&gt;, which is a list of our "group lists."&lt;/p&gt;

&lt;p&gt;So, each list in our kernel consists of shapes that are "alike" by the &lt;code&gt;f&lt;/code&gt; operation, meaning this operation treats shapes &lt;em&gt;the same way&lt;/em&gt;.&lt;br&gt;
But, the thing is that we consider &lt;em&gt;each such nested list&lt;/em&gt; to be a &lt;em&gt;representation&lt;/em&gt; of a conditional f-shape.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In some sense, we can call such representation "homotopy type." But, of course, "homotopy type" is not a standard term of "Homotopy Type Theory." We are just making analogies here.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And here we get another thing from our diagram - the &lt;code&gt;Quotient Set&lt;/code&gt;, which is represented on it as &lt;code&gt;G/K&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;G/K&lt;/code&gt; represents a &lt;em&gt;generalization&lt;/em&gt; of &lt;code&gt;G&lt;/code&gt; by the &lt;code&gt;f&lt;/code&gt; operation.&lt;/p&gt;

&lt;p&gt;And finally, after applying our &lt;code&gt;f&lt;/code&gt; operation, all shapes from our groups changed their colors, so we get a new set.&lt;br&gt;
This new set, denoted as &lt;code&gt;H&lt;/code&gt;, is the set of all shapes turned to another color by the &lt;code&gt;f&lt;/code&gt; operation.&lt;/p&gt;

&lt;p&gt;Now, putting it all together, the fundamental homomorphism theorem states that &lt;code&gt;G/K&lt;/code&gt; behaves the same as &lt;code&gt;H&lt;/code&gt;: a set of different representations of &lt;em&gt;shapes&lt;/em&gt; can be &lt;em&gt;manipulated in the same way&lt;/em&gt; as a single specific shape.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;G/K&lt;/code&gt; is isomorphic to &lt;code&gt;H&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We know this thing as &lt;em&gt;duck typing&lt;/em&gt;: if something looks like a duck, swims like a duck, and quacks like a duck, then we consider it to be a duck, so we can manage it with just one operation: "looks_like," "swims," or "quacks."&lt;/p&gt;




&lt;p&gt;The interesting thing is that Professor Sussman emphasizes &lt;code&gt;G/K&lt;/code&gt;, but not the whole theory as it is.&lt;br&gt;
From his point of view, we are getting another beautiful definition of Abstraction, and it might be formulated like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Abstraction is the principle or scheme of uniting different &lt;em&gt;representations&lt;/em&gt; that behave the same way under a given operation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It looks like this definition varies from the dozens of other definitions of Abstraction, yet it indeed is &lt;em&gt;compatible&lt;/em&gt; with almost any of them.&lt;/p&gt;

&lt;p&gt;Because this idea is &lt;strong&gt;really powerful&lt;/strong&gt; - it allows us to consolidate all the complex relationships between various implementations and their common abstract domain into a single &lt;em&gt;"reflection."&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In fact, &lt;code&gt;ϕ&lt;/code&gt; is the "reflection" of abstraction, whereas &lt;code&gt;G&lt;/code&gt; and &lt;code&gt;G/K&lt;/code&gt; are concrete and abstract subject areas, respectively.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How to live with it
&lt;/h2&gt;

&lt;p&gt;This potential link between "software-ish" abstractions and Abstract Algebra with Type Theories feels really sweet, but does it give us any evident and applicable mechanism which can help us to improve our mostly boring daily coding?&lt;/p&gt;

&lt;p&gt;Well, &lt;em&gt;"evident"&lt;/em&gt; - no. Applicable - I do believe &lt;em&gt;yes&lt;/em&gt;. Let me explain.&lt;/p&gt;

&lt;p&gt;Abstract interpretation does not give us any sharp tools to fight with &lt;em&gt;inaccuracy&lt;/em&gt;.&lt;br&gt;
In the previous post, I found that any correct formal definitions of abstractions (meaning in concrete subjects) essentially every time give us the conclusion - "&lt;em&gt;this&lt;/em&gt; thing could be just &lt;strong&gt;anything&lt;/strong&gt;."&lt;/p&gt;

&lt;p&gt;Not really accurate, when implementing something we want to be as much accurate as possible.&lt;/p&gt;

&lt;p&gt;And still, having in mind such sane definitions of Abstractions one of which we just discovered gives us the possibility to look at the subjective area from a completely different point of view.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Conversely, not having such &lt;em&gt;knowledge&lt;/em&gt; leaves us more "blind," forcing us to move through the &lt;em&gt;space&lt;/em&gt; of some domain in almost complete &lt;em&gt;darkness&lt;/em&gt;. Yes, I do believe it is a good metaphor for our case.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Just try to imagine how many homomorphisms we might end up with by the end of the day, and how diverse they could be even for the same &lt;code&gt;G&lt;/code&gt; if we design our system and its operations carelessly.&lt;/p&gt;

&lt;p&gt;Do you feel it? This is literally the source of nightmares in poorly designed systems.&lt;/p&gt;

&lt;p&gt;Will thinking only in terms of methods and patterns, such as those we have from the GoF, help with this?&lt;/p&gt;

&lt;p&gt;I am not quite sure, because it feels that while we keep talking about patterns and other mainstream concepts, we are losing something really important.&lt;/p&gt;

&lt;h2&gt;
  
  
  The value
&lt;/h2&gt;

&lt;p&gt;So the main conclusion I can extract from today's topic might be obvious to you already.&lt;/p&gt;

&lt;p&gt;I think the most universal and truly safe way of abstracting domain ontology from the very first steps of any project is to define the most rough, generalized, and obvious entities as Abstract Data Types based on &lt;em&gt;operations on them, and not on their "names".&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Of course, we will most likely in every case proceed with detailing the operations on our ADT's, but we &lt;strong&gt;should&lt;/strong&gt; start with the &lt;strong&gt;minimum number of operations designed as possible&lt;/strong&gt;.&lt;/p&gt;




&lt;p&gt;Considering that if &lt;code&gt;f: G -&amp;gt; H&lt;/code&gt; is a homomorphism for a specific operation, it means that &lt;code&gt;f&lt;/code&gt; preserves that operation.&lt;br&gt;
Different operations &lt;em&gt;might&lt;/em&gt; have different corresponding homomorphisms, but, of course, not necessarily for &lt;em&gt;every&lt;/em&gt; possible operation on &lt;code&gt;G&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And still, I am not mathematician, but this "might" feels like... likely.&lt;/p&gt;

&lt;p&gt;I think that &lt;em&gt;amount&lt;/em&gt; of possible homomorphisms is really &lt;em&gt;matters&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Defining, "producing" some type/class will inevitably produce some &lt;em&gt;operation&lt;/em&gt; for them, just because some class should &lt;em&gt;do something&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;When we are not thinking about operations in the first place, we might end up with a cumbersome and complicated type-system/class-hierarchy, trying to dance around polymorphism, method overloading and other ugly things.&lt;/p&gt;

&lt;p&gt;So why should we design systems thinking about phenomena/classes/entities if the operation is the &lt;em&gt;essence of homomorphism&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;From this, it directly follows that &lt;em&gt;calculations&lt;/em&gt; are the "backbone" of every software system, so it should be designed based on &lt;strong&gt;the operations&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It is so stupidly obvious, yet we so often stupidly ignore it, getting lost in vague "things."&lt;/p&gt;




&lt;p&gt;&lt;code&gt;It will not work in designing a complex system for big and complex technical specifications!&lt;/code&gt; - we could fairly argue.&lt;/p&gt;

&lt;p&gt;And it is true. Luckily, we have incredibly useful methods of analysis and designing Abstract Data Types systems that make this process easier and even kind of straightforward.&lt;/p&gt;

&lt;p&gt;Like the one proposed by Bertrand Meyer.&lt;/p&gt;

&lt;p&gt;But it is a completely different story, which I will cover in another blog post.&lt;/p&gt;

&lt;p&gt;Stay tuned, and remember -&amp;gt; &lt;em&gt;calculations&lt;/em&gt;.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>softwaredesign</category>
      <category>abstractalgebra</category>
    </item>
    <item>
      <title>What is Abstraction?</title>
      <dc:creator>Ivan Zakutnii</dc:creator>
      <pubDate>Tue, 07 May 2024 15:52:23 +0000</pubDate>
      <link>https://forem.com/m0n0x41d/what-is-abstraction-44k1</link>
      <guid>https://forem.com/m0n0x41d/what-is-abstraction-44k1</guid>
      <description>&lt;p&gt;The definition of "Abstraction" in software engineering is expressed differently by various people, and often these views directly contradict each other.&lt;/p&gt;

&lt;p&gt;I asked myself - what really is "Abstraction"? How can we precisely define the meaning behind this word?&lt;/p&gt;

&lt;p&gt;Some say that interfaces are an abstraction; others say that interfaces are not an abstraction.&lt;/p&gt;

&lt;p&gt;Some say abstraction is when we find logically related parts of code and separate them into a distinct component; others say that abstraction in software engineering is a process where we focus on individual parts of the model as abstractions.&lt;/p&gt;

&lt;p&gt;What the hell is going on?&lt;/p&gt;

&lt;p&gt;And the crazy part is that these opinions not only come from random engineering blogs but from quite serious academic papers.&lt;/p&gt;

&lt;p&gt;Reading these materials, I realized that I don't fully understand what Abstraction in software engineering really means.&lt;/p&gt;

&lt;p&gt;I caught myself having a very vague idea - in one context, I considered interfaces as abstractions, in another, I could call a simple function an abstraction.&lt;/p&gt;

&lt;p&gt;I can no longer live in the darkness of ignorance, let's figure it out.&lt;/p&gt;




&lt;h2&gt;
  
  
  Limited Forms of Abstraction
&lt;/h2&gt;

&lt;p&gt;First, I would like to start with something quite primitive, which I naively-intuitively took for &lt;em&gt;abstraction&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Function.&lt;/p&gt;

&lt;p&gt;But is a function really an abstraction?&lt;/p&gt;

&lt;p&gt;In the usual sense, functions separate one calculation from another (not always, of course, there are nightmares).&lt;/p&gt;

&lt;p&gt;Functions, in the context of lambda calculus, are often called abstractions. What do they &lt;em&gt;abstract&lt;/em&gt;?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Lambda calculus is a formal system developed by Alonzo Church to formalize computability. And computability is a concept from the theory of algorithms and computer science, which defines which tasks &lt;em&gt;can&lt;/em&gt; be solved using algorithms.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, in lambda calculus functions, parts of an expression are abstracted into hypothetical variables representing generic input for the function.&lt;/p&gt;

&lt;p&gt;For example, the abstraction of a function that takes one argument and returns its product by three would look something like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;λx. x * 3&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This could potentially start a holy war - to consider functions from the perspective of high-minded lambda calculus and from the side of conventional mainstream imperative and OOP programming.&lt;/p&gt;

&lt;p&gt;Nevertheless, in our usual way of programming, by defining a function, we limit one piece of code from another, with brackets or indents, so despite disagreements in views, a function can still be seen as a form of abstraction, but a &lt;em&gt;very limited form&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;A form that does not answer the question - what is abstraction.&lt;/p&gt;




&lt;p&gt;So what about unlimited form?&lt;/p&gt;

&lt;p&gt;Maybe factorizing code into neat modules is a process of extracting abstraction?&lt;/p&gt;

&lt;p&gt;Following the DRY (Don't repeat yourself) principle, we essentially perform &lt;em&gt;anti-unification&lt;/em&gt;, decomposing similar parts of code into separate functions and the like.&lt;/p&gt;

&lt;p&gt;But this is in general lead us to the same example we discussed earlier in the context of a function. If this trick can be called an abstraction, it is still very limited.&lt;/p&gt;

&lt;p&gt;Anti-unification, particularly obsession with it, can lead to one terrible anti-pattern.&lt;/p&gt;

&lt;p&gt;Its name is &lt;em&gt;boxing&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Boxing is when we cram a huge amount of code into one big function or method, the set of parameters for which automatically grows.&lt;/p&gt;

&lt;p&gt;Naively following the idea of de-duplication of code, we can easily loose focus on the specification and logical domain to which these parts of code belong, so we will almost certainly end up with boxing.&lt;/p&gt;

&lt;p&gt;Think about it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Try to find examples of logic, the actual implementation of which will be the same (or almost the same), but the specification, the sense - will have &lt;em&gt;different&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Is it correct to "abstract" the implementations of such logics in our mixed domain into one function/method, even if hidden behind multiple dispatch and similar?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Not Abstractions at All
&lt;/h2&gt;

&lt;p&gt;Well... Abstraction... Where it is...&lt;/p&gt;

&lt;p&gt;Maybe the process of organizing code into separate modules, or in OOP class hierarchy is the process of abstracting things?&lt;/p&gt;

&lt;p&gt;Yes, of course, we can split the code into several modules, hide a lot of logic in class hierarchies, and every time we will return in the project to add a new feature, we will switch between 4 files/modules to remember how and what actually works here.&lt;/p&gt;

&lt;p&gt;Familiar?&lt;/p&gt;

&lt;p&gt;Is this abstraction?&lt;/p&gt;

&lt;p&gt;What's the difference between &lt;code&gt;entity.getTotalSomething()&lt;/code&gt; and &lt;code&gt;getTotalSomething(entity)&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;Looking at this example from a meta level, from a &lt;a href="https://ivanzakutnii.com/blog/Levels-of-reasoning-about-software" rel="noopener noreferrer"&gt;specification level&lt;/a&gt;, I see no difference, they do the same thing.&lt;/p&gt;

&lt;p&gt;So what are we abstracting by organizing long chains of method calls or spreading the class hierarchy across several modules?&lt;/p&gt;

&lt;p&gt;I feel this is not abstraction, but the stirring of implementational "water" in a mortar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Well, What About Interfaces?
&lt;/h2&gt;

&lt;p&gt;An interface, in the broad sense, is essentially a mechanism for grouping several implementations of one "function", allowing a specific call of such function to any of the defined implementations.&lt;/p&gt;

&lt;p&gt;Type classes and parametric polymorphism/generics are intended for the same purpose.&lt;/p&gt;

&lt;p&gt;Well, Abstraction!&lt;/p&gt;

&lt;p&gt;Here we have an implementation hidden behind some "interface", we abstracted it! No? Yes? What's wrong!?&lt;/p&gt;

&lt;p&gt;I don't like it. I don't like that nothing forces all these different implementations to have &lt;em&gt;any relation to each other&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;We can create such a mess that one and the same interface will be implemented in completely different ways, or even make it so that two different implementations will perform completely opposite things.&lt;/p&gt;

&lt;p&gt;And how can this be &lt;em&gt;Abstraction&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;At this stage of the search, I realized what I expect from Abstraction.&lt;/p&gt;

&lt;p&gt;If feels that it should be some sort of &lt;em&gt;specification&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;We can call it "abstract specification", yes, &lt;em&gt;abstract&lt;/em&gt;, but &lt;strong&gt;specification&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It seems that I have finally managed to at least approach the correct, albeit still semi-intuitive definition of abstraction.&lt;/p&gt;

&lt;p&gt;But I still can't find an example of how abstraction looks in code, what it can be expressed by, how it is represented?&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentic Abstraction
&lt;/h2&gt;

&lt;p&gt;What was the starting point for the search - find a concrete, formal definition of Abstraction.&lt;/p&gt;

&lt;p&gt;As I mentioned at the beginning, I tried to find it, but encountered completely opposite formulations in meaning.&lt;/p&gt;

&lt;p&gt;The fact is that I was just looking in the wrong places.&lt;/p&gt;

&lt;p&gt;Taking a broad step away from the mainstream towards Holy Computer Scientists of the Past, I found commonality in definitions, even in seemingly quite different areas - they did not contradict each other and carried a similar sense, which can be expressed as follows:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Abstraction is the mapping of a specific subject area of the &lt;em&gt;dirty&lt;/em&gt;, contradictory, and confusing real world into &lt;em&gt;something pure&lt;/em&gt;, idealized, capable of being described mathematically.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sounds complicated, but the following quote from &lt;em&gt;Edsger W. Dijkstra&lt;/em&gt; clarifies everything:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Being abstract is something &lt;em&gt;profoundly&lt;/em&gt; different from being vague … The purpose of abstraction is not to be vague, but to create a &lt;em&gt;new semantic level&lt;/em&gt; in which one can be absolutely precise.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Aha! Semantic Level!&lt;/p&gt;




&lt;p&gt;So what turns out, if we define abstraction from such a perspective, then it turns out that all the previously considered "mechanisms" of programming are not abstractions after all? Or are they?&lt;/p&gt;

&lt;p&gt;Yes, our OOP classes and even their hierarchy should somehow reflect the subject area, but the &lt;em&gt;purity&lt;/em&gt; (as a concept) of this reflection is highly questionable.&lt;/p&gt;

&lt;p&gt;The same applies to functions, especially &lt;em&gt;dirty&lt;/em&gt; imperative ones filled with side effects.&lt;/p&gt;

&lt;p&gt;The most pure and authentic abstraction that comes to mind is &lt;em&gt;numbers&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The number 108 can be represented in various forms, numeral systems, signs, as a mathematical set...&lt;/p&gt;

&lt;p&gt;Mathematical operations, like addition and multiplication, also have many different implementations, but we never think about them when using calculators.&lt;/p&gt;

&lt;p&gt;And how many interpretations occur around electrical signals, just to see a picture of a doggo on the screen?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;All this looks like when working with good abstractions we don't even realize that we are working with an abstraction.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And it follows that abstraction, in its true and authentic sense, is represented in the code as...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;It is not present at all.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Let me explain my thoughts here.&lt;/p&gt;

&lt;p&gt;Abstraction in principle cannot "be located" somewhere, because it is a &lt;em&gt;representation&lt;/em&gt; it self, in the sense of some &lt;em&gt;pattern&lt;/em&gt;, which we overlay on the modeled world, and not on specific entities of this world.&lt;/p&gt;

&lt;p&gt;It is incorrect to ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Is &lt;code&gt;A&lt;/code&gt; an abstraction of &lt;code&gt;B&lt;/code&gt;?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is more correct to ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Will the &lt;em&gt;mapping&lt;/em&gt; from &lt;code&gt;A&lt;/code&gt; to &lt;code&gt;B&lt;/code&gt; &lt;em&gt;be&lt;/em&gt; an abstraction?&lt;br&gt;
Are all the necessary operations of &lt;code&gt;B&lt;/code&gt; preserved in the abstraction of &lt;code&gt;A&lt;/code&gt;?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, stop. But we have already remembered that there is a "mapping" from each implementation of a function to its interface!&lt;/p&gt;

&lt;p&gt;And to each call of a function, from its implementation!&lt;/p&gt;

&lt;p&gt;Yes, that's true! But it seems we have found the reason why, looking at some interfaces in the code base, we understand nothing, and looking at others, we can make quite correct predictions about what will happen as a result of executing this code - &lt;em&gt;Poor abstraction VS. Good one.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Observing and working with good abstractions, we are able to make quite accurate predictions about the outcomes of the considered code, because "Abstraction" &lt;em&gt;is represented&lt;/em&gt; precisely, expressively, forming that very "&lt;em&gt;semantic level&lt;/em&gt;" mentioned by Dijkstra.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Looking through such &lt;em&gt;quality&lt;/em&gt; representation, we are able to observe the meaning of the code easily.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;And yet, why does abstraction &lt;em&gt;not&lt;/em&gt; look like anything in code?&lt;/p&gt;

&lt;p&gt;Because it's a separate dimension altogether, and it's not that abstraction should be associated with code, but &lt;em&gt;code should be associated with abstraction&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;For example, if we have a method/function whose responsibility is to transfer money from one account to another.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;specifications&lt;/em&gt; of such a function can be described quite differently:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We can describe all the permissible variants of behavior - success or failure of the transaction, under what conditions it is performed or not performed;&lt;/li&gt;
&lt;li&gt;We can describe it as a mapping to an abstract domain to which this function belongs. Most likely such a function will belong to the domain of some "client", hence the abstraction of this function can be described as a mapping to this domain - for example, we can say that this function should perform a transaction, and return a set of transactions after attempting a transfer - including successful and unsuccessful transactions;&lt;/li&gt;
&lt;li&gt;Ultimately, we can simply literally describe the implementation of the function, its purpose and responsibility - "function N attempts to transfer X of something from there to there";&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As we can see, any of these specifications can be used, they themselves are separated not only from the code but also from the domain, it is absurd to claim that our function is a specification (or anything else like a class to which this function belongs as a method, and so on).&lt;/p&gt;

&lt;p&gt;Therefore, it turns out that the code is associated with &lt;em&gt;abstractions&lt;/em&gt;, not the other way around.&lt;/p&gt;

&lt;p&gt;Yes, with &lt;em&gt;a multitude&lt;/em&gt; of abstractions, because such formal descriptions, if you want - &lt;em&gt;ideas&lt;/em&gt;, can be infinite.&lt;/p&gt;

&lt;p&gt;All abstractions are precise in their own way, within the space of meaning they try to impose. Nevertheless, we are able to determine which abstraction is more "correct", more suitable.&lt;/p&gt;

&lt;p&gt;In general, good abstract states always contain less information than concrete ones.&lt;/p&gt;

&lt;p&gt;And this is less obvious than it seems.&lt;/p&gt;

&lt;p&gt;Trying to "hide" already existing "information" in the code behind some "false" abstraction, we engage in anti-unification and slide into boxing.&lt;/p&gt;

&lt;p&gt;Focusing on the meta level, reflecting and designing code in clean, true abstractions, there are fewer chances to slide into boxing because we operate with representation and abstract domains.&lt;/p&gt;




&lt;p&gt;So, I don't know about you, but I think I've found it.&lt;/p&gt;

&lt;p&gt;Abstraction is a "pattern" that we overlay on the modeled external world, resulting in program code.&lt;/p&gt;

&lt;p&gt;Only good abstraction allows, looking at the chains of calls and structures in the code, to predict the meaning and purpose of these calls precisely enough.&lt;/p&gt;

&lt;p&gt;The goal of abstraction is to show whether specific code has some systemic property, whether it is an element of system design, a certain feature, and so forth.&lt;/p&gt;

&lt;p&gt;Abstractions are not expressed in the code by anything or in any way.&lt;/p&gt;

&lt;p&gt;They are very similar to abstract data types. We could even say something like - "Good abstract data types express good abstractions," but that &lt;em&gt;would not be correct&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Good abstractions are &lt;em&gt;completely separate&lt;/em&gt; from the code, and even from the abstract domain.&lt;/p&gt;

&lt;p&gt;Good abstraction is a supra-system entity, which is not always explicitly distinguished in classes/objects, moreover, &lt;em&gt;abstraction does not always need to be somehow distinguished.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>softwaredesign</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>I have tried to love Python</title>
      <dc:creator>Ivan Zakutnii</dc:creator>
      <pubDate>Wed, 17 Apr 2024 15:03:35 +0000</pubDate>
      <link>https://forem.com/m0n0x41d/i-have-tried-to-love-python-1img</link>
      <guid>https://forem.com/m0n0x41d/i-have-tried-to-love-python-1img</guid>
      <description>&lt;p&gt;This isn't clickbait, seriously. I'm not here to trash Python, just sharing my thoughts.&lt;/p&gt;

&lt;p&gt;For the past 9 months, I've been a Platform Engineer at a company that heavily utilizes Python, and it’s not confidential, we are hiring.&lt;/p&gt;

&lt;p&gt;This isn't my first rodeo with Python. In fact, I have quite warm story with this programming language.&lt;/p&gt;

&lt;p&gt;It was essentially my first language, learning to code with it by solving Leetcode-like problems and studying courses on algorithms and data structures, also in Python.&lt;/p&gt;

&lt;p&gt;That was a great time, no regrets.&lt;/p&gt;

&lt;p&gt;Despite Python being dynamicly typed and not being able to encode an algorithm/structure "maturely" with allocs/mallocs like in C, its simplicity and expressiveness made it incredibly useful for jumping into CS, especially for beginners, like I was.&lt;/p&gt;

&lt;p&gt;Because Python is good.&lt;/p&gt;

&lt;h2&gt;
  
  
  I loved Python
&lt;/h2&gt;

&lt;p&gt;Actually, the first programming language I learned and wrote something in wasn’t Python, but rather JS (let’s not talk about school years because programming classes in schools in my country is a disaster).&lt;/p&gt;

&lt;p&gt;Nevertheless, I referred to Python as my first programming language because I really did program a lot in it—various academic/learning stuff, small applied projects, scripts for automating routine tasks on my Linux PC, and during my time as a DBRE.&lt;/p&gt;

&lt;p&gt;I dabbled in Django here and there, just a bit of everything.&lt;/p&gt;

&lt;p&gt;I continued learning CS, programming paradigms, OOP, and I kept loving Python.&lt;/p&gt;

&lt;p&gt;When I moved into DevOps, I was deepening my understanding of programming  paradigms and getting to grips with the foundational pillars of "how things work."&lt;/p&gt;

&lt;p&gt;That was the point in time when I embraced the holiness of &lt;em&gt;static type systems&lt;/em&gt;, and overall, it fels like at this particular point of time I have formed the mindset and development path I should and want to follow.&lt;/p&gt;

&lt;p&gt;Naturally, as I continued working in DevOps, I came across Go and started learning it.&lt;/p&gt;

&lt;h2&gt;
  
  
  I forgot Python
&lt;/h2&gt;

&lt;p&gt;I started working as a DevOps engineer, not an SRE, mostly OPS.&lt;br&gt;
unfortunately, there wasn’t much programming involved, and most of my work time was spent on CI/CD stuff, administering Kubernetes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fcpx0c5i6yzny67zm1qgr.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fcpx0c5i6yzny67zm1qgr.jpeg" alt="Bash Canceled" width="800" height="656"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Almost at the beginning of this period, my son was born, and I didn't have much free time for side projects or significant programming, but all the free time I had continued to invest  to studying Computer Science and Golang.&lt;/p&gt;

&lt;p&gt;I fell in love with Go it almost immediatelly, its blend of simplicity and strictness.&lt;/p&gt;

&lt;p&gt;Over time, as I had more "free time," I programmed some educational projects in Go, a few web apps, a few CLI's, a small k8s listener for cleaning up review environments.&lt;/p&gt;

&lt;p&gt;I continued working as a Dev*&lt;em&gt;OPS&lt;/em&gt;* engineer, dreaming of moving to full-time, or at least "more"-time software development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hello old friend
&lt;/h2&gt;

&lt;p&gt;Eventually, I got the job I was looking for — a place where I could code more while still being in touch with infrastructure.&lt;/p&gt;

&lt;p&gt;Because I still love infrastructure and DevOps; yeah — Platform Engineering. And all in Python.&lt;/p&gt;

&lt;p&gt;I remember my first thoughts when I was getting ready for this job:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Damn, an interpreted language with a dynamic type system, do I... Want to write in Python?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I wanted to code more so badly that I absolutely didn't care what language I would be programming in at my next job, a job where I wouldn’t lose income and could do what I love.&lt;/p&gt;

&lt;p&gt;I don't regret anything; these months I've learned a hell of a lot about Python compared to what I knew before.&lt;/p&gt;

&lt;h2&gt;
  
  
  I tried to love Python again
&lt;/h2&gt;

&lt;p&gt;Before coming to my current position, I didn't know about and didn't use type annotations in Python.&lt;/p&gt;

&lt;p&gt;I didn't know about the marvelous Pydantic; there was a lot I didn't know simply because I had never written anything really mature and big in Python, as I mentioned earlier, using it to learn programming, fundamental things in software design and other CS concepts.&lt;/p&gt;

&lt;p&gt;I learned all this thanks to one person, and I'm grateful to him for everything. This is my onboarding buddy Stanislav Zmiev, and he &lt;em&gt;really&lt;/em&gt; is good at Python.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Just stalk his &lt;a href="https://www.linkedin.com/in/zmievsa/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Well, of course, I can't now see Python without type annotations, I mean for big projects, and even medium ones — anything that's more than a script and has 2+ modules and is not covered by type annotations is &lt;em&gt;abominable&lt;/em&gt; to me; I'm sorry, it just is.&lt;/p&gt;




&lt;p&gt;This doesn't mean that I despise Python or hate it, or don't want to program in it at work, but I can't &lt;em&gt;sincerely love it&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I can't forget all the wonderful (and a few not so wonderful) experiences of programming in Golang. I can't close my eyes to the "interpretability" nature.&lt;/p&gt;

&lt;p&gt;Simply put, I am not ready and will never be ready to dedicate my life to specializing in Python.&lt;/p&gt;

&lt;p&gt;By dedicating my life, I mean programming only in Python, both at work and in side-projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Things get really hot
&lt;/h2&gt;

&lt;p&gt;For the last few months, things have gotten really hot, so hot that I am now a Platform Team Leader. Hold my tea, I need to move some tickets.&lt;/p&gt;

&lt;p&gt;It just turned out, I never aspired to this. But it is what it is, and it automatically means - &lt;em&gt;I code less at work&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I still have free time for side projects, as I did before.&lt;/p&gt;

&lt;p&gt;And here comes a really interesting thing that hit me just a few days ago:&lt;/p&gt;

&lt;p&gt;For months, I racked my brain over the question, "Okay, what should I slap together in Python on GitHub, maybe this? Or that? Or start investing my time in this open-source project as a contributor? I want to code something interesting and usefull as side-hustle..."&lt;/p&gt;

&lt;p&gt;But I was stalling. All this time, I was convinced that I was stalling because I didn't have enough free time, or because I was an inexperienced fool and couldn't figure out the Python codebase. Or because I am lazzy.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It is funny, lazzy, hehe...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hell, &lt;strong&gt;no&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I just don't want to invest my &lt;strong&gt;most&lt;/strong&gt; precious resource, which is  &lt;em&gt;free time&lt;/em&gt; — into programming in Python.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And that's unlikely to change.&lt;/p&gt;

&lt;p&gt;I still am okay to write in it for money, not really matter.&lt;/p&gt;

&lt;h2&gt;
  
  
  My dear blue gopher, are you alive?
&lt;/h2&gt;

&lt;p&gt;For almost a year, I didn't even look at what was happening with Golang, wrote nothing in it, didn't even read much news — I just didn't have time and was &lt;em&gt;forcing&lt;/em&gt; my self in other stuff.&lt;/p&gt;

&lt;p&gt;The language and its ecosystem seem really alive, and I'm sincerely glad about that.&lt;/p&gt;

&lt;p&gt;I have a bunch of good ideas for side projects that I want to implement in Go, starting with simple and smaller ones, just to refresh my memory of the language (though is there much to remember?), and to get better at it.&lt;/p&gt;




&lt;p&gt;Stas, I hope you're not mad at me if you're reading this, but knowing how you love Python and how you can be tempted by clickbaits — chances are there.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Don't smack me up.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Besides Stas, I'm surrounded by a whole bunch of incredibly talented and wonderful Python programmers, Python itself is remarkable in what it's remarkable for, and people codding in Python are just remarcable people.&lt;/p&gt;

&lt;p&gt;I &lt;em&gt;enjoy&lt;/em&gt; writing code in Python at work, and I repeat — I am not a Python hater, at least as long as there are type annotations...&lt;/p&gt;

&lt;p&gt;But it's just not something that can penetrate my strictly typed, tough heart.&lt;/p&gt;

&lt;p&gt;And unfortunately, I can't find in Python things that could melt in, things that aren't in Go, and that I would need.&lt;/p&gt;

&lt;p&gt;Pure OOP? No, that's not one of.&lt;/p&gt;

&lt;h2&gt;
  
  
  But... Rust?
&lt;/h2&gt;

&lt;p&gt;Ok, keep claws away.&lt;/p&gt;

&lt;p&gt;I've tried, I don't want spare time here. At least &lt;em&gt;for now&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I admire all the beauties of the Rust PL, but I am still quite disgusted by a few things.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I do know&lt;/strong&gt; that with time you can live with it and get used to it.&lt;/p&gt;

&lt;p&gt;Humans are really flexible things, but for now... I don't want to, and really can't see any big tradeoffs for me just to rush into Rust and spend a lot of time trying to surpass this huge steep learning curve.&lt;/p&gt;

&lt;p&gt;I want to code things NOW in a statically typed compiled programming language that I already know well, let me code for God's sake.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Gopher is &lt;em&gt;furry enough&lt;/em&gt; for me.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Message
&lt;/h2&gt;

&lt;p&gt;The message is simple, and it is not about Python or Go:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;be kind to yourself and listen to yourself carefully.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In a mad rush to make only the "right" choices, it is really easy to lose the light inside.&lt;/p&gt;

&lt;p&gt;It is easy to forget about impermanence and that there is never just one right choice.&lt;/p&gt;

&lt;p&gt;Things may seem right and profitable at first, second, and even third glance...&lt;br&gt;
Yet they still might not be right or truly profitable.&lt;/p&gt;

&lt;p&gt;Sometimes, it might &lt;em&gt;just&lt;/em&gt; not be your thing — without any analytical reasoning.&lt;/p&gt;

&lt;p&gt;Python is no longer winner of my heart in both senses — based on my honest introspective experience and from &lt;em&gt;just&lt;/em&gt; perspective.&lt;/p&gt;

&lt;p&gt;Do what you love, live your life with joy, and love yourself.&lt;/p&gt;

&lt;p&gt;Peace.&lt;/p&gt;

</description>
      <category>community</category>
    </item>
    <item>
      <title>HighLoad Saga. Part Three: Transaction Processing and Analytics</title>
      <dc:creator>Ivan Zakutnii</dc:creator>
      <pubDate>Tue, 05 Mar 2024 17:22:25 +0000</pubDate>
      <link>https://forem.com/m0n0x41d/highload-saga-part-three-transaction-processing-and-analytics-2m1c</link>
      <guid>https://forem.com/m0n0x41d/highload-saga-part-three-transaction-processing-and-analytics-2m1c</guid>
      <description>&lt;p&gt;When we say "transaction," we refer to a group of operations (read/write in various combinations) that reflect a single logical operation for data handling, executed atomically - either all changes are applied, or the database state is rolled back to its state at the beginning of the transaction.&lt;/p&gt;

&lt;p&gt;Formally, a transaction is expected to possess &lt;em&gt;ACID&lt;/em&gt; properties, which stand for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Atomicity, as already mentioned;&lt;/li&gt;
&lt;li&gt;Consistency,&lt;/li&gt;
&lt;li&gt;Isolation, meaning the ability of one transaction to operate independently from other transactions,&lt;/li&gt;
&lt;li&gt;Durability, implying that if a transaction is successful, its results are permanently fixed in the database, regardless of database management system failures, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, in practice, transaction processing essentially means the ability for clients to perform read and write operations with low latency and the assurance that in case of failure, data corruption or breach of its logical integrity will not occur.&lt;/p&gt;

&lt;p&gt;A basic case of such a "user" transaction is an application using an index to find a small number of records by a specific key. Based on user-provided data, new records are added or existing ones are updated. Most such applications are interactive, leading to the access pattern being termed &lt;em&gt;online transaction processing&lt;/em&gt; (OLTP).&lt;/p&gt;




&lt;p&gt;Database management systems are increasingly used for analytical data processing and in data science, where access patterns and requirements differ significantly.&lt;/p&gt;

&lt;p&gt;A typical analytical query involves selecting a vast number of records, reading only a few columns in each, and calculating aggregate statistical indicators instead of returning raw data to the user.&lt;/p&gt;

&lt;p&gt;To distinguish this database application pattern from transaction processing, it has been named &lt;em&gt;online analytical processing&lt;/em&gt; (OLAP).&lt;/p&gt;

&lt;p&gt;Thus, OLTP implies arbitrary access to data and database write operations with low latency based on client data. Here, the database always reflects the current data state at the moment of the transaction, and the size of such databases can reach terabytes.&lt;/p&gt;

&lt;p&gt;OLAP, on the other hand, usually involves either a long-term group data import, with possible transformation, known as &lt;em&gt;ETL - Extract, Transform, Load&lt;/em&gt;, or processing a continuous stream of certain events. Typically, such a database stores not only current data but also the history of their changes, and its size is measured in petabytes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Warehouses
&lt;/h2&gt;

&lt;p&gt;Interestingly, SQL has proven to be quite flexible; it performs just as well in OLAP as in OLTP. Despite this, not so long ago, a trend emerged for creating special &lt;em&gt;data warehouses&lt;/em&gt;, with large companies starting to move analytics away from OLTP systems to specialized databases.&lt;/p&gt;

&lt;p&gt;A large enterprise has many transaction processing subsystems, each typically complex, supported by a separate team of engineers, and almost always operated independently of others.&lt;/p&gt;

&lt;p&gt;From OLTP systems, we expect high availability and rapid transaction processing because these systems are often critical to business operations.&lt;/p&gt;

&lt;p&gt;Due to these requirements/expectations, we are reluctant to allow business analysts to run analytical queries on these databases simply because these queries are almost always quite complex and resource-intensive, as they involve viewing large data sets, which naturally can adversely affect the performance of other transactions being executed in parallel with such analytical queries.&lt;/p&gt;

&lt;p&gt;A data warehouse, however, is a separate database that analysts can query as they wish, without disrupting business operations.&lt;/p&gt;

&lt;p&gt;The warehouse contains a read-only copy of data from all of the company's OLTP systems. Data is extracted from OLTP databases through periodic data dumps or a continuous stream of data updates, transformed into an analysis-friendly format, cleaned, and then loaded into the warehouse.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is the ETL process.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Data warehouses are specifically optimized for analytical queries.&lt;/p&gt;

&lt;p&gt;The indexing algorithms that work well for OLTP are not as effective for responding to analytical queries.&lt;/p&gt;

&lt;p&gt;The data warehouse model is often relational, as SQL generally suits analytical queries well.&lt;/p&gt;

&lt;p&gt;Thus, data warehouses and relational OLTP databases look similar (since both have an SQL interface for queries).&lt;/p&gt;

&lt;p&gt;However, the internal structure of these systems differs.&lt;/p&gt;

&lt;h2&gt;
  
  
  OLAP Data Models
&lt;/h2&gt;

&lt;p&gt;In OLTP, a wide variety of data models cater to different needs.&lt;/p&gt;

&lt;p&gt;However, in analytics, the variety of models is much smaller.&lt;/p&gt;

&lt;p&gt;Many data warehouses operate on a standardized pattern known as the &lt;em&gt;star schema&lt;/em&gt;, also known as &lt;em&gt;dimensional modeling&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In this model, the &lt;em&gt;fact table&lt;/em&gt; is at the center. Each row of this table reflects an event that occurred at a specific point in time.&lt;/p&gt;

&lt;p&gt;Depending on the nature and objectives of the business, each row of this table may reflect a page view, a user action in the system, a product purchase, and so on.&lt;/p&gt;

&lt;p&gt;Facts usually enter the warehouse as separate events, which is very convenient, but we must always be aware of the potential for growth in the size of this table and be prepared for it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In large corporations, data warehouses store petabytes of transaction history, with fact tables making up a large portion of this history.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If a row in the fact table reflects an &lt;em&gt;event&lt;/em&gt;, then &lt;em&gt;dimensions&lt;/em&gt; correspond to the characteristics of "who," "what," "where," "when," "how," and "&lt;/p&gt;

&lt;p&gt;why" related to this event.&lt;/p&gt;

&lt;p&gt;In short, part of the columns in the fact table are the event's attributes, and the rest are foreign keys to dimension tables.&lt;/p&gt;

&lt;p&gt;That's why this schema is called a star.&lt;/p&gt;




&lt;p&gt;In an alternative version of the "star" pattern, known as the "snowflake" schema, data is further divided into sublevels.&lt;/p&gt;

&lt;p&gt;Instead of storing all dimension information in one table, the "snowflake" schema uses separate tables, sub-dimensions, for more detailed categorization of data.&lt;/p&gt;

&lt;p&gt;The "snowflake" schema provides a higher degree of data normalization compared to the "star" schema, making data more structured and orderly. However, the "star" pattern is more commonly used in business analytics for its convenience and simplicity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Columnar Storage
&lt;/h2&gt;

&lt;p&gt;In a data warehouse, tables can be extremely wide. This applies to both the fact table, which may have hundreds of columns, and dimension tables, which can become very wide due to the many metadata columns required for analytics.&lt;/p&gt;

&lt;p&gt;If fact tables contain trillions of rows and petabytes of data, storing and querying them efficiently becomes a challenging task.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Dimension tables are usually much smaller, so we will focus on fact storage.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Although fact tables often exceed 100 columns in width, a typical query to the warehouse only accesses a few columns!&lt;/p&gt;

&lt;p&gt;In most OLTP databases, storage is organized row-wise: all values from one table row are stored next to each other. Document-oriented databases are similarly structured: the entire document is usually stored as a continuous byte sequence.&lt;/p&gt;

&lt;p&gt;If OLAP databases followed a similar row-wise implementation, the storage subsystem would need to load all rows (each with 100 or more columns) into memory, parse them syntactically, and filter the result based on the conditions specified in the query.&lt;/p&gt;

&lt;p&gt;This is highly inefficient.&lt;/p&gt;

&lt;p&gt;The idea of &lt;em&gt;columnar storage&lt;/em&gt; emerged as an optimization: storing values from the same column together, rather than from the same row.&lt;/p&gt;

&lt;p&gt;If each column is stored, for example, in a separate file, a query only needs to read and parse the required columns.&lt;/p&gt;

&lt;p&gt;However, column-based data storage requires that the files of all columns contain rows in the same order. Consequently, to assemble a whole row, e.g., the 128th row, one must take the 128th element from all the column files and compile them together.&lt;/p&gt;

&lt;p&gt;Besides loading only the columns needed for a query from the disk, it's also possible to further reduce disk bandwidth requirements by compressing the data.&lt;/p&gt;

&lt;p&gt;Since columns often contain similar and semantically close values, they compress very well.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Depending on the data contained in a column, various compression methods are applied. One of the methods, particularly effective for data warehouses, is bitmap encoding.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Sorting Order in Columnar Storage
&lt;/h3&gt;

&lt;p&gt;In columnar storage, the order of row storage, at first glance, seems to have little effect.&lt;/p&gt;

&lt;p&gt;In a previous post, we discussed the simplest write operation - appending to the end of a file. And this looks like a good option for such storage.&lt;/p&gt;

&lt;p&gt;But we can set the write order, just like in SSTables, and use it as an indexing mechanism.&lt;/p&gt;

&lt;p&gt;Obviously, sorting each column separately makes no sense, as it would then be unclear which column elements belong to the needed row.&lt;/p&gt;

&lt;p&gt;Remember, in columnar storage, we can assemble rows only because we know: the nth element of one column and the nth element of another column always correspond to the same nth row.&lt;/p&gt;

&lt;p&gt;So, if we want to sort something here, we need to sort the rows as a whole, despite the fact that data is stored by columns.&lt;/p&gt;

&lt;p&gt;Such sorting is useful if specific columns can be selected for sorting the table, based on knowledge of the most frequently executed queries.&lt;/p&gt;

&lt;p&gt;Sorting also helps to compress columns effectively. If the main sorting column has a small number of different values, long sequences of repeating identical values will appear after sorting. Simple encoding, for example, using bitmap schemes, can compress such a column to just a few kilobytes even with billions of rows in the table. Compression works best for the first sorting key. The second and third keys will be more mixed, hence, they won't have such long sequences of repeating values.&lt;/p&gt;

&lt;p&gt;For different queries, different sorting orders are better, so why not store differently sorted copies of the data?&lt;/p&gt;

&lt;p&gt;This serves as both data replication and optimization of typical queries.&lt;/p&gt;

&lt;p&gt;Having multiple sorting orders in columnar storage is akin to a group of secondary indexes in a traditional row-based storage.&lt;/p&gt;

&lt;p&gt;But an important difference is that in row-based storage, each row is stored in one place - in an unordered file or a clustered index, and secondary indexes only contain pointers to the corresponding rows.&lt;/p&gt;

&lt;p&gt;In columnar storage, there are usually no pointers to data - only columns containing values.&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing to Columnar Storage
&lt;/h3&gt;

&lt;p&gt;Data warehouses allow for various forms of optimization, as most of the load falls on the voluminous read-only queries performed by analysts. Columnar storage, compression, and sorting significantly speed up the execution of such queries. However, these warehouses have a serious drawback in the form of complicating write operations.&lt;/p&gt;

&lt;p&gt;The approach of updating data in place, used by B-trees, is impossible in the case of compressed columns. If it's necessary to insert rows in the middle of a sorted table, most likely, all column files will need to be rewritten. Insertion must update all columns in a coordinated manner -- since rows are defined by their position in the column.&lt;/p&gt;

&lt;p&gt;Fortunately, we've already explored a good solution to this problem: LSM-trees. Everything is first written to storage in RAM, where data is added to a sorted structure and prepared for disk writing.&lt;/p&gt;

&lt;p&gt;It doesn't matter whether the storage in RAM is columnar or row-based. Once enough data has accumulated, it is merged with the disk's column files and written in blocks to new files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Memory Bandwidth and Vectorized Processing
&lt;/h2&gt;

&lt;p&gt;A significant bottleneck for data warehouse queries is that they have to scan millions or billions of rows, which becomes the bandwidth limitation for moving data from disk to memory.&lt;/p&gt;

&lt;p&gt;Moreover, for analytical databases, the efficient use of the data transfer rate from RAM to the CPU cache, avoiding various kinds of branch prediction errors and "bubbles" in the CPU instruction processing pipeline, as well as using the vector instructions (SIMD) of modern processors, becomes a problem.&lt;/p&gt;

&lt;p&gt;Besides reducing the volumes of data loaded from the disk, columnar storage schemes can also efficiently use CPU cycles.&lt;/p&gt;

&lt;p&gt;For example, the query processing subsystem may take a portion of data that fits well into the L1 cache of the processor and pass through it in a continuous loop.&lt;/p&gt;

&lt;p&gt;The processor can execute such a loop much faster than code containing many function calls and conditions for each processed record.&lt;/p&gt;

&lt;p&gt;Column compression allows more rows for one column to fit in the same volume of L1 cache.&lt;/p&gt;

&lt;p&gt;For working directly with such portions of compressed columnar data, classic bitwise OR/AND operations can be used. This method is known as vectorized processing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Aggregation: Data Cubes and Materialized Views
&lt;/h2&gt;

&lt;p&gt;Not all data warehouses for OLAP tasks are necessarily columnar: row-based DBs and several other architectures are also used. However, columnar warehouses perform much faster for arbitrary analytical queries, so their popularity is growing rapidly.&lt;/p&gt;

&lt;p&gt;Another important feature of data warehouses is materialized aggregate indicators. Queries to warehouses often include aggregation functions, such as COUNT, SUM, AVG, MIN, or MAX in SQL.&lt;/p&gt;

&lt;p&gt;If the same aggregation functions are used in a variety of different queries, it would be wasteful to recalculate raw data from scratch every time.&lt;/p&gt;

&lt;p&gt;Why not cache some of the aggregate indicators most frequently used in queries if the data hasn't changed?&lt;/p&gt;

&lt;p&gt;One way to create such a cache is a &lt;em&gt;materialized view&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In the relational data model, it is often described similarly to a regular view: it is a table-like object which content is the result of some query.&lt;/p&gt;

&lt;p&gt;The difference is that a materialized view is an actual copy of the query results, written to disk, while a virtual view is just a shorthand form of writing for queries.&lt;/p&gt;

&lt;p&gt;When reading from a virtual view, the SQL engine dynamically unfolds it into the underlying query, and then fully executes this unfolded query.&lt;br&gt;
OLAP Cube&lt;/p&gt;

&lt;p&gt;In the case of changes to the data used in the aggregated query, it's necessary to update the materialized view, as it represents a denormalized copy of these data.&lt;/p&gt;

&lt;p&gt;The DBMS can perform updates automatically, but such manipulations increase the cost of write operations, so materialized views are rarely used in OLTP databases.&lt;/p&gt;

&lt;p&gt;However, in warehouses, where the main load falls on read operations, it makes sense to actively use them. Whether they actually improve the performance of read operations depends on the specific case.&lt;/p&gt;

&lt;p&gt;A common case of a materialized view is a data cube, or OLAP cube. It represents a grid of aggregate indicators, grouped by different dimensions.&lt;/p&gt;

&lt;p&gt;For example, each fact in the fact table has foreign keys only to two dimension tables, let's say, date and product.&lt;/p&gt;

&lt;p&gt;We can build a two-dimensional table with products on one axis and dates on the other. Each cell contains an aggregate indicator of an attribute of all facts with such a combination of date and product.&lt;/p&gt;

&lt;p&gt;Then, we can apply the same aggregating function along each row or column and get totals, reduced by one dimension.&lt;/p&gt;

&lt;p&gt;In general, facts often have more than two dimensions.&lt;/p&gt;

&lt;p&gt;Formally, setting up a five-dimensional hypercube is much more difficult, but the idea remains the same: each cell contains sales for the corresponding combination of date, product, store, advertising campaign, and customer. These values can then be sequentially grouped by each dimension.&lt;/p&gt;

&lt;p&gt;The advantage of materialized cubes is that certain queries will be executed very quickly because the data for them were essentially pre-calculated.&lt;/p&gt;

&lt;p&gt;For example, if we need to know the total sales volume for few days back by each partner, we just may take a look at the totals by the corresponding dimension - there's no need to analyze millions of rows.&lt;/p&gt;

&lt;p&gt;The drawback of this approach is that data cubes don't have the same flexibility as queries to raw data.&lt;/p&gt;

&lt;h2&gt;
  
  
  OLAP vs OLTP
&lt;/h2&gt;

&lt;p&gt;In practice, except for analysis tasks, mainly OLTP systems are used, implying potentially a huge number of queries.&lt;/p&gt;

&lt;p&gt;Programmers optimize the load, trying to affect a limited number of rows in each query.&lt;/p&gt;

&lt;p&gt;The client program requests records using a specific key, and the storage subsystem employs an index to find data with the corresponding key.&lt;/p&gt;

&lt;p&gt;The bottleneck here usually becomes the time to move to the required position on the disk.&lt;/p&gt;

&lt;p&gt;Data warehouses are mainly used by business analysts and handle much fewer queries than OLTP systems.&lt;/p&gt;

&lt;p&gt;However, almost all OLAP queries are usually very resource-intensive and require viewing millions or even billions of rows in a short time.&lt;/p&gt;

&lt;p&gt;The bottleneck here usually is a disk bandwidth. For this reason, columnar storages are gaining popularity for this type of task.&lt;/p&gt;

&lt;p&gt;In OLTP, there are two main approaches to building data storage subsystems.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The log-structured approach, which only allows appending data to files and deleting outdated files, not updating a written file. These are SSTables, LSM-trees, HBase systems, Lucene, etc.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The in-place update approach, which views the disk as a set of pages of a certain size that allow rewriting. The main representative of this philosophy is B-trees, used in all major relational databases, as well as many non-relational ones.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The relatively new thing is log-structured storage systems. Their main idea consists of systematically converting any disk write into a sequential one, providing higher write throughput due to the performance characteristics of hard drives and solid-state drives.&lt;/p&gt;

&lt;p&gt;And for this technical points analytical OLAP tasks differ so significantly from OLTP tasks: when queries require sequential scanning of a large number of rows, indexes play no special role. Instead, encoding data very compactly to minimize the volume of data read from the disk becomes much more important.&lt;/p&gt;




&lt;p&gt;But we will talk about data encoding formats and few other things next time :)&lt;br&gt;
Thanks for reading.&lt;/p&gt;

</description>
      <category>systemdesign</category>
    </item>
    <item>
      <title>HighLoad Saga. Part Two, Chapter 2: Data Storage and Processing Subsystems</title>
      <dc:creator>Ivan Zakutnii</dc:creator>
      <pubDate>Sun, 25 Feb 2024 06:46:41 +0000</pubDate>
      <link>https://forem.com/m0n0x41d/highload-saga-part-two-chapter-2-data-storage-and-processing-subsystems-5gh5</link>
      <guid>https://forem.com/m0n0x41d/highload-saga-part-two-chapter-2-data-storage-and-processing-subsystems-5gh5</guid>
      <description>&lt;p&gt;So, databases are data storage systems. Essentially, they should solve just two tasks: save the data received from the client and provide data in the future as a response to specific requests.&lt;/p&gt;

&lt;p&gt;A good question is - should an application developer know how the DBMS is structured internally? Specifically, how is data stored and how does the search for this data work?&lt;/p&gt;

&lt;p&gt;It sounds like a closed question, but I believe the correct answer goes something like this: By default, a developer &lt;em&gt;should not&lt;/em&gt; know how to implement a data storage subsystem from scratch (we are talking about us - JSON bricklayers, not system developers hired on the project to implement a new ultra-fast DBMS, of course), but the developer should understand the practical significance of different storage subsystems, their pros and cons, to be able to choose the suitable one for the project being developed.&lt;/p&gt;

&lt;p&gt;Well, in general, the short answer to this closed question then is a yes :)&lt;/p&gt;

&lt;p&gt;We need to have at least a rough, conceptual idea of the storage mechanisms' structure to select and optimally configure the DBMS for our load.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic Data Structures in DBMS: Key-Value
&lt;/h2&gt;

&lt;p&gt;Typically, a database stores unique keys, each corresponding to some value. In general, the types of these values can vary.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;simplest&lt;/em&gt; form of implementing the key-value model is to write data in a file as lines that contain pairs of keys and values serialized into a text/string format.&lt;/p&gt;

&lt;p&gt;The performance of adding a new key-value pair in this case is high if keys are always generated as uniquely new (current timestamp, for example) and updates to existing records are not allowed.&lt;/p&gt;

&lt;p&gt;Under such conditions, we simply append new lines at the end of the file.&lt;/p&gt;

&lt;p&gt;Many DBMSs use a (mainly for specific purposes) essentially &lt;em&gt;similar&lt;/em&gt; mechanism called &lt;em&gt;journaling&lt;/em&gt;: there is a file, called a journal (or log, or whatever), and it is intended only for appending data at its end.&lt;/p&gt;

&lt;p&gt;Obviously, in practice, such logging is implemented in a more complex manner, as it requires managing concurrent access to the journal, controlling disk space, creating backups, handling errors, and correcting corrupted records. Nonetheless, the fundamental principle remains the same.&lt;/p&gt;

&lt;p&gt;To optimize the use of disk space, journals are usually divided into segments of a certain size. Once a segment reaches this size, its file is "closed", and subsequent data are recorded in a new segment.&lt;/p&gt;

&lt;p&gt;In addition to this, there often exists so-called &lt;em&gt;compaction&lt;/em&gt; technology for existing segments, which involves discarding duplicate keys from the journal and keeping only the latest version for each key.&lt;/p&gt;

&lt;p&gt;Many logging/journaling technologies implement similar mechanics of segmentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Indexes
&lt;/h2&gt;

&lt;p&gt;So, we manage to write to the journal efficiently, but reading a value by key would work terribly inefficiently in the case of a large number of records in our journal.&lt;/p&gt;

&lt;p&gt;We end up with linear complexity &lt;em&gt;O(n)&lt;/em&gt;, because each time we would have to search through our entire "database" from start to finish looking for the required key.&lt;/p&gt;

&lt;p&gt;To ensure efficient searching for any key in our storage system, we need another data structure - an &lt;em&gt;index&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The main essence of an index is that it stores additional metadata, a kind of "pointers", which help to find the necessary records much faster.&lt;/p&gt;

&lt;p&gt;Typically, the ability to add and remove indexes in most DBMSs does not affect the content (actual data) of the database, but modifying indexes affects the &lt;em&gt;performance of queries&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Maintaining indexes leads to additional costs, especially when the database writes new values to disk, because indexes need to be updated each time to keep them up to date with the new data.&lt;/p&gt;

&lt;p&gt;In such a scenario, it is practically impossible to outperform the efficiency of simply appending data to the end of a file (since this is the simplest of all possible disk write operations).&lt;/p&gt;

&lt;p&gt;This is an important compromise in data storage systems: well-chosen indexes speed up read queries but slow down writing.&lt;/p&gt;

&lt;p&gt;This is why DBMSs do not index everything by default but offer developers the chance to choose indexes manually, based on their knowledge of query patterns typical for the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hash Indexes
&lt;/h2&gt;

&lt;p&gt;The key-value data model is somewhat "similar" to the dictionary (map, etc.) data structure.&lt;/p&gt;

&lt;p&gt;Or rather, dictionaries are usually implemented as hash maps or hash tables.&lt;/p&gt;

&lt;p&gt;Since hash maps are used for in-memory data structures, and such structures are extremely convenient, how about using hash maps for indexing data on disk?&lt;/p&gt;

&lt;p&gt;Imagine we are still dealing with our simplest storage, which operates solely by appending data to the end of a journal file.&lt;/p&gt;

&lt;p&gt;We start to build and keep a hash map in memory, where each key corresponds to a physical offset (relative address) in the data file - essentially a pointer to the specific location where the value is located.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;File operations in every OS allow the operation of positioning the cursor within the file with byte accuracy - the seek operation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As a result, when adding a new "key-value" pair to the file, our hash map is updated to reflect the address of the data just written. This implementation works efficiently both when inserting new keys and when updating the values of existing ones.&lt;/p&gt;

&lt;p&gt;Just in case - hash maps can serve not only for indexing pointers to stored values but also for indexing any metric of interest.&lt;/p&gt;

&lt;p&gt;For example, a video URL might serve as a key, and the value could be the number of views, which increases each time someone requests playback.&lt;/p&gt;

&lt;p&gt;With this type of load, the number of write operations will be high, but there will not be too many unique keys. Therefore, in such a scenario, it is perfectly acceptable to store all keys and values in memory.&lt;/p&gt;

&lt;p&gt;This concept is implemented by in-memory databases, which entirely (or in large blocks) fit into RAM, achieving very high performance.&lt;/p&gt;

&lt;p&gt;To ensure their reliability, data from memory is regularly dumped as snapshots to a fast (hopefully) disk.&lt;/p&gt;

&lt;p&gt;So, we run into the fact that the hash table must fit into memory, and if there are &lt;em&gt;really&lt;/em&gt; a lot of keys in the database - we're in trouble.&lt;/p&gt;

&lt;h2&gt;
  
  
  SSTables and LSM Trees
&lt;/h2&gt;

&lt;p&gt;Developing our scenario further, let's try to change the format of &lt;em&gt;segment&lt;/em&gt; files (remember the journal segments?).&lt;/p&gt;

&lt;p&gt;The new requirement is to sort the sequences of "key-value" pairs by key.&lt;/p&gt;

&lt;p&gt;The first thing that comes to mind is that this requirement will prevent us from writing sequentially to the file, and it seems that other problems related to working with the journal may arise.&lt;/p&gt;

&lt;p&gt;A valid concern, but first, let's consider the advantages we get.&lt;/p&gt;

&lt;p&gt;The new format we are describing is called &lt;em&gt;SS-table&lt;/em&gt;, or &lt;em&gt;"sorted string table."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The implementation of SS-tables also requires ensuring that each key is unique in the combined segment. We already talked about this at the beginning, mentioning that key uniqueness is achieved through a compaction process.&lt;/p&gt;

&lt;p&gt;In an SS-table, to find a specific key in the file, it's no longer necessary to store indexes of all keys in memory because all keys are sorted, and access to them is pretty fast.&lt;/p&gt;

&lt;p&gt;Moreover, the compaction of segments is performed efficiently and simply, even if the file sizes exceed the available memory space (The approach is similar to that used in the merge sort algorithm).&lt;/p&gt;

&lt;p&gt;Sorted data structures can be stored on disk, but it's much easier to do this in memory - using tree-like data structures such as red-black or balanced binary trees.&lt;/p&gt;

&lt;p&gt;Using such structures, we can very quickly add keys in any sequence and then read them in the desired order.&lt;/p&gt;

&lt;p&gt;One significant and obvious problem remains - in the event of a fatal database failure, recently written data, which has not yet been written to disk, is &lt;em&gt;lost&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;A common practice to combat this problem is to maintain a separate journal on disk, into which all new data being written is &lt;em&gt;added immediately&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Such a journal does not need to be sorted in some specific way, because its main and only purpose is to restore the data structure in memory when the database starts up after a failure.&lt;/p&gt;




&lt;p&gt;Storage subsystems based on the principle of merging and compacting sorted files are often called &lt;em&gt;LSM storage subsystems&lt;/em&gt;, derived from Log-Structured Merge-Tree, or &lt;em&gt;LSM-Tree&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;An LSM-tree-based algorithm may perform slowly when searching for missing keys, because it requires scanning the entire structure in memory firstly, and then scanning all segments up to the oldest one, reading each of them from the disk before it can be confirmed that the key is missing.&lt;/p&gt;

&lt;p&gt;To optimize such access, storage subsystems often employ a &lt;em&gt;Bloom filter&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;A Bloom filter is a probabilistic data structure that provides an efficient way to test whether an element is a member of a set. It can quickly tell if a key is definitely not in the database or might be, with a small chance of false positives. The beauty of a Bloom filter lies in its ability to perform these checks in nearly constant time, significantly reducing the need for disk reads when searching for keys that do not exist in the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  B-Trees
&lt;/h2&gt;

&lt;p&gt;Although journaled indexes are a popular technology, they are not the most common type of indexes.&lt;/p&gt;

&lt;p&gt;The most widely used index structure is the &lt;em&gt;B-tree&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;B-trees were introduced 50 years ago and remain the default implementation of indexes in nearly all relational database management systems to this day.&lt;/p&gt;

&lt;p&gt;Many non-relational databases also use B-trees for indexing.&lt;/p&gt;

&lt;p&gt;B-trees have one thing in common with SSTables: they store key-value pairs sorted by key, allowing for efficient key lookups and range queries.&lt;/p&gt;

&lt;p&gt;While journaled indexes divide the database into segments of variable size and always write them to disk sequentially, B-trees divide the DB into blocks or pages of a fixed size, typically 4 kilobytes, reading or writing one page at a time.&lt;/p&gt;

&lt;p&gt;This structure is better suited to low-level hardware since disks in the file system are also divided into blocks of a fixed size.&lt;/p&gt;

&lt;p&gt;Each page has an address, allowing pages to refer to other pages.&lt;/p&gt;

&lt;p&gt;These references form a tree of pages. One of the pages is designated as the root of the B-tree - it is the starting point for any key search in the index.&lt;/p&gt;

&lt;p&gt;The root page contains several keys and links to child pages. Each of the child pages corresponds to a continuous range of keys, and special keys, located between pointers, indicate the boundaries of these ranges.&lt;/p&gt;

&lt;p&gt;The number of links to child pages on a specific B-tree page is called the &lt;em&gt;branching factor&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The algorithm ensures that the tree remains balanced - the depth of a B-tree with &lt;em&gt;n&lt;/em&gt; keys will be &lt;em&gt;O(log n)&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;For most databases, trees of three or four levels deep are sufficient, so the DBMS does not have to follow many page references to find the required one.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A four-level tree of 4 KB pages with a branching factor of 500 can store up to 256 terabytes of information.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;The basic write operation of a B-tree involves rewriting its page on disk with new data.&lt;/p&gt;

&lt;p&gt;It is assumed that such rewriting does not change the page's location in the tree, meaning all references to it remain unchanged.&lt;/p&gt;

&lt;p&gt;This is a significant difference from journaled indexes, for example, from LSM-trees, where files are only appended to, and outdated files are gradually deleted in the compaction process, but existing files are not modified.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimization Challenges
&lt;/h2&gt;

&lt;p&gt;In-place page updating involves certain complexities - when multiple threads access a B-tree simultaneously, careful management of concurrent access is required; otherwise, a thread may traverse the tree in an inconsistent state.&lt;/p&gt;

&lt;p&gt;The mechanism for correct concurrent access is usually implemented using &lt;em&gt;latches&lt;/em&gt; - a lightweight type of lock.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The key difference between latches and locks lies in their scope and duration. &lt;strong&gt;Locks&lt;/strong&gt; are typically used to manage access to database data at a higher level, such as rows or tables, and can be held for longer periods, such as the duration of a transaction. They are primarily concerned with ensuring data integrity across concurrent transactions. &lt;strong&gt;Latches&lt;/strong&gt;, on the other hand, are used at a lower level to protect specific data structures in memory and are held for very short durations. Their main purpose is to ensure the consistency and integrity of these structures during concurrent access, rather than managing broader transactional data access.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The journaled approach is simpler in this sense, as merging occurs in the background, without interfering with incoming requests, and with periodic atomic replacement of old segments with new ones.&lt;/p&gt;

&lt;p&gt;But we are not always dealing with the rewriting of only one page.&lt;/p&gt;

&lt;p&gt;As pages are limited to a small size, values exceeding this size are split across multiple pages. Consequently, when updating such a value (or inserting a new, large value), we need to update/write more than one page, as well as rewrite their parent page to update links to all child pages.&lt;/p&gt;

&lt;p&gt;In the event of a database failure at the moment when only part of the page is written, we end up with a corrupted index. A typical reason is the appearance of an orphan page, which has no parent because the process of updating links was not completed during the failure.&lt;/p&gt;

&lt;p&gt;To ensure DB resilience, implementations usually include an additional data structure on disk - a &lt;em&gt;write-ahead log&lt;/em&gt;, also known as a redo log.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Journals, here we go again.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This log is a file or series of files intended only for appending information, where all modifications to B-trees (and modifications to &lt;em&gt;other&lt;/em&gt; DBMS pages that have not yet been written to disk - i.e., stored in the DBMS buffers) are recorded before being applied to the pages themselves.&lt;/p&gt;

&lt;p&gt;When the database recovers from a failure, this log is used to restore and bring the B-tree to a consistent state.&lt;/p&gt;

&lt;p&gt;Some DBMSs also apply a write-on-copy scheme. The modified page is written to a different location with the creation of new versions of parent pages in the tree, pointing to this new location. This approach is also useful for managing concurrent access.&lt;/p&gt;

&lt;p&gt;For optimization purposes, space on pages is sometimes saved by storing not the entire key but only its shortened version.&lt;/p&gt;

&lt;p&gt;Additional pointers can also be added to the tree. For example, each leaf page may link to its left and right pages at the same level, allowing for an orderly traversal of keys without returning to parent pages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pros and Cons of Different Approaches
&lt;/h2&gt;

&lt;p&gt;Generally, LSM-trees usually perform better in writing, while B-trees excel in reading.&lt;/p&gt;

&lt;p&gt;Reading in LSM-trees is slower because it involves scanning several different data structures and SSTables at various stages of compaction.&lt;/p&gt;

&lt;p&gt;A B-tree-based index must write each piece of data several times, at least twice - once in the write-ahead log and then onto the tree page itself.&lt;/p&gt;

&lt;p&gt;Let's not forget about page splitting...&lt;/p&gt;

&lt;p&gt;Journaled indexes also rewrite data several times due to repeated compaction and merging of SSTables, but clearly, these operations do not occur with every new entry write.&lt;/p&gt;

&lt;p&gt;The effect where one database write operation leads to multiple disk write operations is known as &lt;em&gt;write amplification&lt;/em&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If we have our server in a closet, this factor is very important to consider, especially when using SSDs, which can only rewrite blocks a limited number of times before they wear out :)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In applications requiring large volumes of writing, the bottleneck may be the speed at which the database writes data to disk. In this case, write amplification directly impacts performance - the more the storage subsystem writes to disk, the fewer write operations per second it can perform within the available disk bandwidth.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other Index Structures
&lt;/h2&gt;

&lt;p&gt;Key-value indexes are very similar to the primary key index in the relational model.&lt;/p&gt;

&lt;p&gt;A primary key uniquely identifies a single row in a relational table, one document in a document-oriented database, or one vertex in a graph database.&lt;/p&gt;

&lt;p&gt;In relational databases, it's possible to create multiple secondary indexes in one table using the &lt;strong&gt;CREATE INDEX&lt;/strong&gt; command. The presence of such indexes is often critically important for the efficient execution of &lt;strong&gt;JOIN&lt;/strong&gt;'s.&lt;/p&gt;

&lt;p&gt;The main difference from the primary key is that secondary keys are &lt;em&gt;not unique by default&lt;/em&gt; - meaning that several rows can share the same key.&lt;/p&gt;

&lt;p&gt;This problem can be easily solved by making all keys unique, adding a row identifier to them.&lt;/p&gt;

&lt;p&gt;Both B-trees and journaled indexes can be used as secondary indexes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Composite Indexes
&lt;/h3&gt;

&lt;p&gt;We previously discussed indexes that map a single key to a value.&lt;/p&gt;

&lt;p&gt;However, they are not sufficient in cases where a query involves multiple table columns or document fields simultaneously.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Composite indexes&lt;/em&gt; are used to optimize such queries.&lt;/p&gt;

&lt;p&gt;The most common type of composite index is the concatenated index, which simply combines several fields into one key by attaching one column to another.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When creating an index, its description specifies the order in which the fields are concatenated.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Suppose we have a Books table containing the &lt;em&gt;Author&lt;/em&gt;, &lt;em&gt;Title&lt;/em&gt;, and &lt;em&gt;Year&lt;/em&gt; columns. We want to optimize queries that filter books by both author and publication year simultaneously.&lt;/p&gt;

&lt;p&gt;In this case, we can create a composite index that concatenates the &lt;em&gt;Author&lt;/em&gt; and &lt;em&gt;Year&lt;/em&gt; fields into one key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_author_year&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;Books&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Year&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When creating such an index, we specify that the &lt;em&gt;Author&lt;/em&gt; field comes first, followed by the &lt;em&gt;Year&lt;/em&gt;. This index will be effective for queries like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Books&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Stephen King'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="nb"&gt;Year&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1994&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, if we want to find all books published in 1837 without specifying the author, our index will be less useful because the search starts with the &lt;em&gt;Author&lt;/em&gt; field, which is not specified in this case.&lt;/p&gt;

&lt;p&gt;In such cases, the DBMS query planner might choose another index or even opt for scanning the entire table as the search algorithm, which could be less efficient.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multidimensional Indexesi
&lt;/h3&gt;

&lt;p&gt;Multidimensional indexes are another way to query multiple columns at once, especially important for working with geospatial data.&lt;/p&gt;

&lt;p&gt;For example, a mapping service might use a database with the latitude and longitude coordinates of all stored objects (e.g., restaurants, barbershops, schools, etc.).&lt;/p&gt;

&lt;p&gt;When processing a query, it needs to find all objects located within a rectangular area on the map (the frontend determines which area of the map the user has selected).&lt;/p&gt;

&lt;p&gt;Then, two-dimensional queries, for example, for searching schools, might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;schools&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;latitude&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;7128&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;latitude&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;7484&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt;
      &lt;span class="n"&gt;longitude&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;74&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0060&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;longitude&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;73&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;9732&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Standard indexes based on B-trees or LSM-trees cannot provide efficient execution of such a query. They would efficiently return either all schools within a certain latitude range with arbitrary longitude or all schools within a certain longitude range but at any point between the North and South poles - and cannot do both simultaneously.&lt;/p&gt;

&lt;p&gt;To support geospatial queries, specialized spatial indexes are used, such as &lt;em&gt;R-trees&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Multidimensional indexes can be used not only for geographical coordinates but also for optimizing any multidimensional queries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Searching for products by color (RGB): to find products within a specific range of colors;&lt;/li&gt;
&lt;li&gt;Movie or book recommendation systems: Multidimensional indexes can be used to optimize queries in recommendation systems, where each item (e.g., movie or book) is rated on several criteria, such as genre, release year, and rating;&lt;/li&gt;
&lt;li&gt;Warehouse inventory management: In databases tracking warehouse inventories, multidimensional indexes can be used to optimize queries on multiple attributes simultaneously, such as product type, batch size, and expiration date.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And so on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Full-Text Search and Fuzzy Indexes
&lt;/h3&gt;

&lt;p&gt;The indexes we've discussed are excellent for searching for precise values within specific ranges.&lt;/p&gt;

&lt;p&gt;However, they lack the capability to search for keys that may contain errors or resemble each other — for instance, words with spelling errors. To manage such fuzzy queries, alternative approaches are necessary.&lt;/p&gt;

&lt;p&gt;Full-text search systems typically offer the ability to extend a word search by including synonyms, disregarding various grammatical forms of the word, searching for words located near each other in the text, and supporting additional functions based on linguistic text analysis. They utilize a range of technologies, from Levenshtein algorithms for finding words within a certain "edit distance" to machine learning methods.&lt;/p&gt;

&lt;h2&gt;
  
  
  In-Memory Databases
&lt;/h2&gt;

&lt;p&gt;In many scenarios, data sets are relatively small, making it feasible to store them entirely in memory.&lt;/p&gt;

&lt;p&gt;This concept led to the development of in-memory databases, aptly named for their storage method.&lt;/p&gt;

&lt;p&gt;Some key-value stores, like Memcached, utilize a cache only when data loss is permissible in the event of a machine reboot.&lt;/p&gt;

&lt;p&gt;However, other in-memory databases aim to ensure full data persistence through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writing change logs to disk,&lt;/li&gt;
&lt;li&gt;Periodically saving state snapshots to disk,&lt;/li&gt;
&lt;li&gt;Replicating the memory state to other machines.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally, employing uninterruptible power supplies or NVDIMM memory, which retains data through server restarts, is encouraged.&lt;/p&gt;

&lt;p&gt;Paradoxically, the fact that entirely in-memory-operated databases do not need to read data from disk &lt;strong&gt;does not always give them a significant performance edge&lt;/strong&gt; over traditional database management systems.&lt;/p&gt;

&lt;p&gt;Modern data storage systems and operating systems are capable of caching recently used data in memory, thereby eliminating the need to read it from disk if the server's memory capacity is sufficiently large for such a DBMS and if most operations involve reading data.&lt;/p&gt;

&lt;p&gt;In-memory databases may offer increased speed by reducing the overhead associated with serializing data structures for disk storage.&lt;/p&gt;

&lt;p&gt;Moreover, in-memory DBs present unique advantages in terms of implementing data models that are challenging or inefficient to execute using disk-based indexes.&lt;/p&gt;

&lt;p&gt;For instance, Redis provides interfaces for various data structures, like priority queues and sets, making it resemble a database. All information is kept in memory, streamlining the implementation of such interfaces.&lt;/p&gt;

&lt;p&gt;The architecture of in-memory databases can also scale relatively easily to support data sets larger than the available memory, avoiding the overhead typical of architectures that &lt;em&gt;actively&lt;/em&gt; rely on disk writing.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;anti-caching&lt;/em&gt; technique permits the removal of infrequently used data from memory to disk, with the possibility of reloading it when needed. This method is similar to how operating systems manage virtual memory and swap files, but with more precise control at the level of individual records, allowing databases to manage memory more efficiently than the operating system. Nevertheless, for this strategy to be effective, it's essential for the indexes to fit entirely in memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Coming Up Next
&lt;/h2&gt;

&lt;p&gt;In this post, we've explored various index structures, delving into their essential nature and the impact of their inner workings. This knowledge should aid us in better understanding data storage options, enabling us to select and optimize storage solutions that bring the most benefits to our projects. However, our exploration doesn't end here. Stay tuned, as next time we will take a look on transaction processing and analytics.&lt;/p&gt;




&lt;p&gt;Cover photo by &lt;a href="https://www.pexels.com/photo/magnifying-glass-on-book-4494642/" rel="noopener noreferrer"&gt;Nothing Ahead&lt;/a&gt;&lt;/p&gt;

</description>
      <category>systemdesign</category>
    </item>
    <item>
      <title>HighLoad Saga. Part Two, Chapter 1: Storing the Data</title>
      <dc:creator>Ivan Zakutnii</dc:creator>
      <pubDate>Fri, 09 Feb 2024 17:53:52 +0000</pubDate>
      <link>https://forem.com/m0n0x41d/highload-saga-part-two-chapter-1-storing-the-data-hf2</link>
      <guid>https://forem.com/m0n0x41d/highload-saga-part-two-chapter-1-storing-the-data-hf2</guid>
      <description>&lt;p&gt;Modern tools and technologies are no longer fit in classical DBMS categoris.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;DBMS is database management system. The software like PostgreSQL and so on.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These technologies, optimized for broad scenarios, continue to blur traditional boundaries.&lt;/p&gt;

&lt;p&gt;For instance, in-memory key-value systems like Redis can surprisingly serve as message queues, while systems like Kafka offer message queues with added storage reliability.&lt;/p&gt;

&lt;p&gt;Addressing complex System Design challenges involves decomposing them into smaller problems and applying a wide range of tools.&lt;/p&gt;

&lt;p&gt;This includes using additional layers like Memcached for caching or Elasticsearch and Sphinx for full-text searching, which operate separately from DBMS.&lt;/p&gt;

&lt;p&gt;Synchronizing these caches is crucial to ensure data consistency and non-contradictory results for users, and maee it so is a responsibility of our software.&lt;/p&gt;




&lt;p&gt;Modern apps often build on a layered data model approach, each layer presenting its data to be correctly understood by the next.&lt;/p&gt;

&lt;p&gt;This complexity increases as systems scale, despite abundant resources on relational data modeling.&lt;/p&gt;

&lt;p&gt;So, choising right data model is greatly influences the functionality of the software based on it, it is extremely important to choose a model that is suitable for your specific task.&lt;/p&gt;

&lt;p&gt;Yet, the industry frequently defaults to relational models and SQL, and I am woundetind = if this approach is always appropriate?&lt;/p&gt;

&lt;p&gt;Lets take a look at some of approaches focused on data storage and query execution, probably we will find the answer for this questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  SQL data model
&lt;/h2&gt;

&lt;p&gt;The well-known SQL data model, created in 1970 by Edgar Codd, organizes data into tables.&lt;/p&gt;

&lt;p&gt;However, it faces an &lt;em&gt;object-relational mismatch&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Almost every mainstream framework, especially in web development, offer an object-oriented approach, which implies mapping the structure of objects to the fields of relational database tables.&lt;/p&gt;

&lt;p&gt;As a result, we have a clumsy intermediate layer between objects and the database model which is pretty hard to formalize good enought.&lt;/p&gt;

&lt;p&gt;This disjunction between fundamentally different computational models is also referred as &lt;em&gt;impedance mismatch.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Impedance mismatch is only partially addressed by ORM tools.&lt;br&gt;
They tries to eliminate the differences between the two models, but just not able to do it, introducing more complexity and inefficiency.&lt;/p&gt;
&lt;h2&gt;
  
  
  Oh, hello there - NoSQL!
&lt;/h2&gt;

&lt;p&gt;That the poing where NoSQL data bases coming onto the stage.&lt;/p&gt;

&lt;p&gt;What is NoSQL? Well, it is database management systems which are sotres unstructured &lt;em&gt;documents&lt;/em&gt;. That why such database called as document-oriented.&lt;/p&gt;

&lt;p&gt;JSON-representations are have better &lt;em&gt;locality&lt;/em&gt; compared to a multi-table schema.&lt;/p&gt;

&lt;p&gt;Locality means that all related data for some record is stored in the same document, it just all in plase.&lt;/p&gt;

&lt;p&gt;Whereas, to be able fetch all needed data about some entity we will probably forced to make several queries or come up with some complicated "all-side" joins of that entity table with all related tables.&lt;/p&gt;



&lt;p&gt;For example relations "one-to-many" of some user profile with all the other related, detailed characteristics imply a &lt;em&gt;tree-like&lt;/em&gt; data structure, and the JSON representation makes this structure explicit and clear.&lt;/p&gt;

&lt;p&gt;While storing all the related data in one JSON documents might be handy, there is a significant drawback of &lt;em&gt;data duplication&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In a JSON document-oriented database, you might store each employee's information in a single document like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"employeeId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"E123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Pepe the Frog"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"department"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Platform Engineering"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Backend Developer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"contact"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pepe.feels-good@froggie.croack"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"phone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"555-1234-666-99"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"projects"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"projectId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"P1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"projectName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Project Alpha"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"projectId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"P2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"projectName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Project Beta"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This results in massive data duplication:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The details of "Project Alpha" and "Project Beta" are replicated in full for each employee involved, rather than being stored once and referenced many times.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If project details change (e.g., the name or deadline), they must be updated in every employee document that includes that project, increasing the risk of inconsistent data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This duplication can lead to increased storage requirements and slower queries as the amount of redundant information grows, especially in larger organizations with many employees working on the same projects&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;The idea of eliminating duplication lies at the heart of the database &lt;em&gt;normalization concept&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;But data normalization often requires organizing &lt;em&gt;"many-to-one"&lt;/em&gt; relationships, which fit poorly into the document model.&lt;/p&gt;

&lt;p&gt;In relational databases, it is considered normal to refer to rows in other tables by a unique identifier, since performing joins is not a problem.&lt;/p&gt;

&lt;p&gt;In document-oriented databases, tree-like structures do not need joins, and if support for joins exists, it is often very weak.&lt;/p&gt;

&lt;p&gt;When the database managment sysmtem does not support joins, they must be performed in the application code through a set of database queries.&lt;/p&gt;




&lt;p&gt;There is another relations type - &lt;em&gt;"many-to-many"&lt;/em&gt;. Suth relations a regular case in relation database, but for NoSQL there has been an endless discussion on how best to represent "many-to-many" relationships.&lt;/p&gt;

&lt;p&gt;Essentially, in the scope of this question we are going back to the very first days of digital database systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  It is CO-DA-SYL, Harry!
&lt;/h2&gt;

&lt;p&gt;Back in the 1960 there was hierarhical DBMS called &lt;em&gt;IBM IMS&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;It was a pretty good with "one-to-many", just like modern document-oriented dbs, but "many-to-many" was not an option for IBM IMS at all, as also it was not supports joins at all.&lt;/p&gt;

&lt;p&gt;There was a huge volume of denormalized, duplicated data, and developers spend a lot of time trying to controll data actuallity.&lt;/p&gt;

&lt;p&gt;To get rid of hierarhical model limitations there was proposed a few solutions. The two most know - relational model (later formed as SQL) and &lt;em&gt;network model&lt;/em&gt; CODASYL.&lt;/p&gt;

&lt;p&gt;The CODASYL model became a generalization of the hierarchical model, in whose tree-like structure each record had exactly one parent record.&lt;/p&gt;

&lt;p&gt;In the network model, each record could have multiple parents. This made it possible to model "many-to-one" and "many-to-many" relationships.&lt;/p&gt;

&lt;p&gt;The "links" between records in the network model was not like relational model's foriegn keys, but more something more like a pointers in programming languges.&lt;/p&gt;

&lt;p&gt;The only way to query some recoed was a traverse from the root record all way through these links.&lt;/p&gt;

&lt;p&gt;It the trivial scenario it was the same as traversing a linked list - with a O(N) complexity we are going throug all the nodes until we find one we are looking for.&lt;/p&gt;

&lt;p&gt;But CODASYL was designed for more than just simple scenarios. As mentioned, it accommodated "many-to-many" relationships, meaning there were multiple "routes" leading to a single record.&lt;/p&gt;

&lt;p&gt;Developers working with the network model had to be mindful of these complex access paths, as modifying the database's schema could easily disrupt them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fst83w17kdeyubl1rxp5h.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fst83w17kdeyubl1rxp5h.gif" alt="mindblow" width="220" height="213"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Eventually, members of the CODASYL committee acknowledged that navigating through an n-dimensional data space has a very high algorithmic complexity.&lt;/p&gt;

&lt;p&gt;So, on the other hand, relational model exposed all the data as it was - tables is just a set of tuples, without any complex "labirints" should be passed to access required data.&lt;/p&gt;

&lt;h2&gt;
  
  
  But here we go again...
&lt;/h2&gt;

&lt;p&gt;Despite historical challenges with hierarchical and network models, NoSQL databases are gaining more and more popularity.&lt;/p&gt;

&lt;p&gt;Why is that?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;We need to scale, and sclae &lt;em&gt;fast!&lt;/em&gt;. Much quicker than relation database are capable of. We need to process very large data sets or have very high write throughput;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We might need to make a specialized querie that are poorly supported by the relational model;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We are disappointed by the limitations of relational schemas and we want more dynamic and expressive data models.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Document-oriented databases now are going back to hierarhical model, primarily in terms of storing nested records - "one-to-may" stored in the &lt;em&gt;parental&lt;/em&gt; record, without orginizing any separate "table".&lt;/p&gt;

&lt;p&gt;But is we want to come up with "many-to-many", or "many-to-one", well... relational and document-oriented databases are &lt;em&gt;not significantly differs&lt;/em&gt; in their approaches.&lt;/p&gt;

&lt;p&gt;In the both of these scenarios query for requeired element is performed with the help of unique idenitfier, which is a &lt;em&gt;"foreign key"&lt;/em&gt; in the relational model, and a &lt;em&gt;"document reference"&lt;/em&gt; in the document model.&lt;/p&gt;

&lt;p&gt;This identifier is resolved at read time through joins or additional queries.&lt;/p&gt;

&lt;p&gt;Document-oriented database are not following the path laid by CODASYL, and &lt;em&gt;generally&lt;/em&gt;, I guess it is fine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Document versus Relation
&lt;/h2&gt;

&lt;p&gt;If our application's data structure is document-like (i.e., it represents tree of "one-to-many" relations, usually loaded all at once), using a document model is likely a good choice.&lt;/p&gt;

&lt;p&gt;The relational model enhances productivity for "many-to-one" and "many-to-many" relationships, albeit with some theoretical limitations. For instance, it requires shredding—breaking down the document-like structure into multiple tables.&lt;/p&gt;

&lt;p&gt;When choosing a document-oriented database, consider the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Direct references to nested elements within a document are not possible in the document model. This requires encoding paths in a way reminiscent of the hierarchical model, which usually isn't a problem if document nesting is limited to few levels  (2-3).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;-- The lack of robust support for joins in document-oriented databases may or may not be an issue, depending on the project. For instance, "many-to-many" relationships may not be necessary for a content management system where articles are stored with embedded comments. But this limitation becomes significant somthing like social networking apps, where complex "many-to-many" relationships between users and groups are essential.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reducing the number of necessary joins through denormalization is possible, but it requires extra maintenance work to keep the denormalized data consistent.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Database Schemes
&lt;/h2&gt;

&lt;p&gt;Most document-oriented databases, as well as relational databases in scenarious of using JSON or XML records, do not enforce a specific schema to define a unified data structure within documents, unlike the strict structure defined for tables in the classical relational data model.&lt;/p&gt;

&lt;p&gt;The absence of a schema allows us to insert any keys and values into a document, and when reading these documents, we have completely no guarantees or annotations explaining what fields are present in it.&lt;/p&gt;

&lt;p&gt;Thus, document-oriented databases are often referred to as unstructured, schemaless, among other terms.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fm3i7f0h8wu7uxyc3wj6i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fm3i7f0h8wu7uxyc3wj6i.png" alt="Joking" width="800" height="1022"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Such schema-less approach is called &lt;em&gt;schema-on-read&lt;/em&gt;. It implies that the data structure is implicit, and data interpretation occurs when records are read from the database.&lt;/p&gt;

&lt;p&gt;Conversely, &lt;em&gt;schema-on-write&lt;/em&gt; is the traditional approach of relational databases, always having a clearly defined, guaranteed data schema that all written data must match.&lt;/p&gt;

&lt;p&gt;Schema-on-read is similar to dynamic type checking in programming languages, whereas schema-on-write resembles static type checking.&lt;/p&gt;

&lt;p&gt;There's no definitive for this "holywar" question, answering which approach is better.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Locality and Queries
&lt;/h2&gt;

&lt;p&gt;Documents are stored in the database as continuous strings, serialized into JSON, XML, or their binary variants (BSON in MongoDB, or JSONB in PostgreSQL).&lt;/p&gt;

&lt;p&gt;If an application frequently needs access to the entire document, data locality offers advantages: a single query can retrieve the whole hierarchical structure.&lt;/p&gt;

&lt;p&gt;If data is spread across multiple tables, multiple queries and index searches are needed to fully extract them, requiring more disk operations and taking more time.&lt;/p&gt;

&lt;p&gt;Data locality is only advantageous when large parts of a document are needed at once because the database has to load the entire document, even if only a part of this document is needed.&lt;/p&gt;

&lt;p&gt;This is inefficient.&lt;/p&gt;

&lt;p&gt;Updating a document, almsot in all cases requires rewriting it entirely.&lt;/p&gt;

&lt;p&gt;Therefore, it's generally recommended to minimize document size and avoid write operations that increase size when working with document-oriented databases.&lt;/p&gt;

&lt;p&gt;These performance limitations significantly narrow the scenarios where document-oriented databases are more useful than relational ones.&lt;/p&gt;

&lt;p&gt;And because of it relational and document-oriented databases are becoming more similar.&lt;/p&gt;

&lt;p&gt;It seems the future lies in hybrids of relational and document models.&lt;/p&gt;

&lt;p&gt;PostgreSQL, in particular, handles JSON brilliantly &lt;em&gt;already&lt;/em&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  Query Methods - SQL
&lt;/h2&gt;

&lt;p&gt;SQL is a declarative language that was a breakthrough for the relational model, while IMS and CODASYL used imperative code for their queries.&lt;/p&gt;

&lt;p&gt;In declarative query languages, a "pattern" of expected data must be described. We set conditions for the resulting data and, posddibly, conditions on how this data must be transformed (sorted, for example).&lt;/p&gt;

&lt;p&gt;SQL does not give a heck on how the results should be achieved.&lt;/p&gt;

&lt;p&gt;The decision on which indexes and join methods to use and in what specific order to execute parsed parts of a query is made only by the database managment system's &lt;em&gt;query optimizer&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Thus, declarative languages are well-suited for parallel execution as they define only a results pattern, and not the algorithm for obtaining them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Graph Data Models
&lt;/h2&gt;

&lt;p&gt;Through our exploration, we've seen that hierarchical models don't fit well with "many-to-many" relationships, and relational models only handle the basics of these interactions. As data connections become more complex, using a graph to model this information feels more intuitive and efficient.&lt;/p&gt;

&lt;p&gt;In a graph, there are two primary elements: vertices (also known as nodes or entities) and edges (also referred to as relationships or arcs).&lt;/p&gt;

&lt;p&gt;Graphs can shape various data types, not just those that are similar. They are powerful in combining different kinds of object data within one storage solution.&lt;/p&gt;

&lt;p&gt;Take Meta as an example, which uses a unified graph structure filled with diverse vertices and edges. Vertices symbolize individuals, places, occasions, system entries, and comments made by users. Edges reveal connections like friendships, specific login locations, comment authorship, event attendance, and more.&lt;/p&gt;

&lt;p&gt;Think of a graph database as composed of two relational tables—one for vertices and another for edges. Properties of vertices and edges might be stored using data types like json. Information on the starting (head_vertex) and ending (tail_vertex) points of edges is kept, making it possible to swiftly access all incoming or outgoing edges related to a particular vertex by querying the respective fields in the edges table.&lt;/p&gt;

&lt;p&gt;Key features of this model include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Any vertex can link to another through an edge without restrictions on the types of connections.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It's possible to identify both incoming and outgoing edges for any vertex, allowing for navigation through the graph in both directions. This is why indexes are placed on both the tail_vertex and head_vertex columns in the edges table.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Utilizing varied labels for different relationship types enables the storage of diverse information in a single graph, keeping the model's structure clean and organized.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;We've taken a good look at how we keep and find our digital information. We've seen that old ways like SQL databases aren't always enough anymore, and that's why we're seeing more use of graph databases. These new methods are like a fresh coat of paint in a world that's always changing and growing, just like how Meta is doing things now.&lt;/p&gt;

&lt;p&gt;And we're not done yet! Next time, we'll dig into the nitty-gritty of how data is stored, talk about what "key-value" means, and explore all sorts of indexes. We'll also tackle big concepts like SSTables, LSM-trees, and B-trees, and we’ll try to make sense of how to make everything run faster and smoother. So, stay with us as we keep on exploring this HighLoad adventure. There's a lot more to come!&lt;/p&gt;




&lt;p&gt;Cover photo by &lt;a href="https://pixabay.com/photos/files-paper-office-paperwork-stack-1614223/" rel="noopener noreferrer"&gt;Ag Ku&lt;/a&gt;&lt;/p&gt;

</description>
      <category>systemdesign</category>
      <category>highload</category>
    </item>
  </channel>
</rss>
