<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>alchemmist — Eosp</title><link>https://alchemmist.xyz/tags/eosp/</link><description>Последние записи в блоге alchemmist</description><generator>Hugo 0.164.0</generator><language>en</language><atom:link href="https://alchemmist.xyz/tags/eosp/index.xml" rel="self" type="application/rss+xml"/><lastBuildDate>Wed, 11 Feb 2026 16:02:00 +0300</lastBuildDate><item><title>Typing in Python</title><link>https://alchemmist.xyz/articles/typing-python/</link><pubDate>Wed, 11 Feb 2026 16:02:00 +0300</pubDate><dc:creator>alchemmist</dc:creator><guid>https://alchemmist.xyz/articles/typing-python/</guid><description>This article covers two key questions. Why use typing in Python, a language that lets you write without it? And how do you write typed Python code correctly? We will quickly go through the essential tools so that after reading, you can consciously start writing typed Python programs. It is not that hard.
# Dynamic vs Static So what is typing? For many Python developers, this can feel new, because most classic languages such as C, Java, Rust, and many others were originally designed as statically typed languages. But what does that mean? Let’s look at a small C example:</description><content:encoded><![CDATA[<p>This article covers two key questions. Why use typing in Python, a
language that lets you write without it? And how do you write typed
Python code correctly? We will quickly go through the essential tools
so that after reading, you can consciously start writing typed Python
programs. It is not that hard.</p>

<h2 id="dynamic-vs-static">
  <a class="link" href="#dynamic-vs-static">
    #
  </a>
  Dynamic vs Static
</h2>

<p>So what is typing? For many Python developers, this can feel new,
because most classic languages such as C, Java, Rust, and many others
were originally designed as statically typed languages. But what does
that mean? Let&rsquo;s look at a small C example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">sum</span><span class="p">(</span><span class="kt">int</span> <span class="n">a</span><span class="p">,</span> <span class="kt">int</span> <span class="n">b</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">a</span> <span class="o">+</span> <span class="n">b</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%d</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="nf">sum</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">20</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// printf(&#34;%d\n&#34;, sum(&#34;10&#34;, 20));
</span></span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>This code works and prints <code>30</code>. But note that the last line is
commented out. If we uncomment it and compile again, we will get an
error log like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plaintext" data-lang="plaintext"><span class="line"><span class="cl">error: passing argument 1 of ‘sum’ makes integer from pointer without cast
</span></span><span class="line"><span class="cl">   10 |     printf(&#34;%d\n&#34;, sum(&#34;10&#34;, 20));
</span></span><span class="line"><span class="cl">      |                        ^~~~
</span></span><span class="line"><span class="cl">      |                        |
</span></span><span class="line"><span class="cl">      |                        char *
</span></span><span class="line"><span class="cl">note: expected ‘int’ but argument is of type ‘char *’
</span></span></code></pre></div><p>The log says that the function parameter expected <code>int</code> but got
<code>char *</code> (<em>for simplicity, think of it as a string</em>). At first glance,
this may not look surprising for Python developers, because this Python
code would also fail:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">sum</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">a</span> <span class="o">+</span> <span class="n">b</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="nb">sum</span><span class="p">(</span><span class="s2">&#34;10&#34;</span><span class="p">,</span> <span class="mi">20</span><span class="p">))</span> <span class="c1"># TypeError</span>
</span></span></code></pre></div><p>So what is the difference? Let&rsquo;s tweak both examples in C and Python,
and call <code>sum</code> with two strings:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="nb">sum</span><span class="p">(</span><span class="s2">&#34;10&#34;</span><span class="p">,</span> <span class="s2">&#34;20&#34;</span><span class="p">))</span> <span class="c1"># &gt; 1020</span>
</span></span></code></pre></div><p>Here we get no error, because polymorphism works and string addition is
valid. But what happens in C?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%d</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="nf">sum</span><span class="p">(</span><span class="s">&#34;10&#34;</span><span class="p">,</span> <span class="s">&#34;20&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>You cannot even compile this program. You get the same error log as
before. Notice how we defined <code>sum</code> in C: we explicitly set input
argument types to <code>int</code>. That means arguments of any other type cannot
be passed into this function. This is static typing. Static typing also
requires a type for each variable and prevents changing variable types
after declaration. In other words, the type is fixed.</p>
<p>The second group is dynamically typed languages: Python, Lua,
JavaScript, and others. In those languages, the variable type is not
strictly fixed and can change during execution.</p>

<h2 id="benefits-of-typing">
  <a class="link" href="#benefits-of-typing">
    #
  </a>
  Benefits of Typing
</h2>

<p>Now to the question: why do we need typing if Python already works well
without it? First, speed. At a low level (<em>whatever representation you
use</em>), types are always needed. If we do not write them explicitly, it
only means someone else does that for us (<em>for example, a virtual
machine</em>), which requires resources. That is reflected in language speed
rankings:</p>
<p><img
    src="/images/pl-rating_hu_96ba15e059292ea.webp"
    width="1600"
    height="1079"
    class="content-img" style="width:900px"
    alt=""
    loading="lazy"
    decoding="async"
  /></p>
<p>In this
<a href="https://github.com/niklas-heer/speed-comparison">rating</a>, Python is at
the bottom. Will typed Python fix that? Unfortunately,
<a href="https://bernsteinbear.com/blog/typed-python/#fnref:simple-annotations">no</a>.
Python was not and still is not a statically typed language. Type
annotations in Python remain optional. You can omit them, and the Python
interpreter does not enforce them at runtime.</p>
<p>This brings us to the second benefit of types: code quality.</p>
<blockquote class="markdown-blockquote">
  <p>&ldquo;The goal of typing in Python is to help development tools find
errors in Python code bases through static analysis, without running
code tests.&rdquo;</p>
<p>Luciano Ramalho, &ldquo;Fluent Python&rdquo;</p>

</blockquote>
<p>Extending this idea, typing goals are:</p>
<ul>
<li>Early error detection, before runtime and before production failures.</li>
<li>&ldquo;Test extraction&rdquo;: correct typing can reduce the number of tests,
which are often harder to write and maintain than types. Tests can
then focus on business logic, not primitive mismatches.</li>
<li>Better readability: <a href="https://peps.python.org/pep-0020/">PEP 20</a>
says &ldquo;Explicit is better than implicit.&rdquo; When reading code, function
signatures are often enough without diving into implementation.</li>
<li>Better IDE workflow with more hints and warnings.</li>
<li>Better architecture quality: types force cleaner abstractions.</li>
</ul>

<h2 id="how-to-write-typed-code">
  <a class="link" href="#how-to-write-typed-code">
    #
  </a>
  How to Write Typed Code
</h2>

<p>Before code, let&rsquo;s separate three important concepts: interface,
abstract class, and protocol. What is the difference?</p>
<ul>
<li><strong>Interface</strong> is a class where all methods are abstract, with no
implementation details.</li>
<li><strong>Abstract class</strong> is a class with both abstract and implemented
methods.</li>
<li><strong>Protocol</strong> is an implicit interface.</li>
</ul>
<p>The first two are straightforward. Let&rsquo;s focus on the last one. In
Python, an interface pattern is usually implemented through inheritance:
a class implementing an interface inherits from that interface class. A
class implementing a protocol does not have to inherit from it and may
not even know about it.</p>

<h3 id="primitives">
  <a class="link" href="#primitives">
    ##
  </a>
  Primitives
</h3>

<p>Let&rsquo;s finally look at how to write typed code. Start with a simple
function that repeats a string <code>n</code> times.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">multi_string</span><span class="p">(</span><span class="n">string</span><span class="p">,</span> <span class="n">n</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">string</span> <span class="o">*</span> <span class="n">n</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">multi_string</span><span class="p">(</span><span class="s2">&#34;cat&#34;</span><span class="p">,</span> <span class="mi">3</span><span class="p">))</span> <span class="c1"># &gt; catcatcat</span>
</span></span></code></pre></div><p>This function takes a string and a number and returns a string built by
concatenating the original string with itself <code>n</code> times. Now type it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">multi_string</span><span class="p">(</span><span class="n">string</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">n</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">string</span> <span class="o">*</span> <span class="n">n</span>
</span></span></code></pre></div><p>Syntax is simple: input parameter types go after a colon, output type
goes after an arrow. Your IDE can now highlight errors if inputs are
wrong or if returned values are handled incorrectly.</p>
<p><img src="/images/typing-demo-1.gif" class="content-img" style="width:700px" alt="" loading="lazy" decoding="async" /></p>
<p>You can annotate all common primitives this way:
<code>str</code>, <code>int</code>, <code>bytes</code>, <code>float</code>, <code>Decimal</code>, <code>bool</code>.</p>

<h3 id="union-types">
  <a class="link" href="#union-types">
    ##
  </a>
  Union Types
</h3>

<p>In real code there are more complex cases where a function should accept
multiple types, but not arbitrary ones. In those cases use unions:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">normalize</span><span class="p">(</span><span class="n">data</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="nb">bytes</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="nb">bytes</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">data</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&#34;utf-8&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">data</span>
</span></span></code></pre></div><p>Here <code>normalize</code> accepts either a string or bytes and always returns a
UTF-8 string. With <code>union</code> (<code>|</code>), you can list multiple types, but you
should use this carefully. If you feel like writing a long union of ten
types, pause and reconsider. We will cover ways to handle such cases
below.</p>
<p>Unions are also valid for return types, but mostly for optional
results. Example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">parse_int</span><span class="p">(</span><span class="n">value</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">int</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="ow">not</span> <span class="n">value</span><span class="o">.</span><span class="n">isdigit</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">int</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
</span></span></code></pre></div><p>This means the function result is optional. It may return <code>int</code>, or may
not return one if parsing fails. But annotating returns as <code>int | str</code>
or other unrelated combinations is usually bad practice. It becomes
unclear how to process the result. In practice, return either a concrete
type with known methods/attributes, or <code>None</code>. Breaking this rule tends
to complicate code.</p>

<h3 id="collection-specification">
  <a class="link" href="#collection-specification">
    ##
  </a>
  Collection Specification
</h3>

<p>Besides primitives, you can and should annotate collections. Avoid plain
<code>data: list</code>; specify element types too. Use square brackets:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">format_user</span><span class="p">(</span><span class="n">user</span><span class="p">:</span> <span class="nb">tuple</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">int</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">name</span><span class="p">,</span> <span class="n">score</span> <span class="o">=</span> <span class="n">user</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">score</span><span class="si">}</span><span class="s2"> points&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">average</span><span class="p">(</span><span class="n">values</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">float</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="nb">float</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">values</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">sum</span><span class="p">(</span><span class="n">values</span><span class="p">)</span> <span class="o">/</span> <span class="nb">len</span><span class="p">(</span><span class="n">values</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">total_count</span><span class="p">(</span><span class="n">counters</span><span class="p">:</span> <span class="nb">dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">int</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">sum</span><span class="p">(</span><span class="n">counters</span><span class="o">.</span><span class="n">values</span><span class="p">())</span>
</span></span></code></pre></div><p>Of course, this does not cover all production scenarios. In
<a href="/articles/typing-python/#typeddict">TypedDict</a>, <a href="/articles/typing-python/#namedtuple">NamedTuple</a>, and
<a href="/articles/typing-python/#dataclass">Dataclass</a>, we extend this toolkit.</p>

<h3 id="mapping-and-mutablemapping">
  <a class="link" href="#mapping-and-mutablemapping">
    ##
  </a>
  Mapping and MutableMapping
</h3>

<p><code>Mapping</code> and <code>MutableMapping</code> are abstract base classes for dict-like
structures. <code>Mapping</code> guarantees read-only behavior (keys, values,
iteration), while <code>MutableMapping</code> says the object is mutable.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">collections.abc</span> <span class="kn">import</span> <span class="n">Mapping</span><span class="p">,</span> <span class="n">MutableMapping</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">read_config</span><span class="p">(</span><span class="n">cfg</span><span class="p">:</span> <span class="n">Mapping</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">str</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">cfg</span><span class="p">[</span><span class="s2">&#34;DATABASE_URL&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">patch_config</span><span class="p">(</span><span class="n">cfg</span><span class="p">:</span> <span class="n">MutableMapping</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">str</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">cfg</span><span class="p">[</span><span class="s2">&#34;DEBUG&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&#34;1&#34;</span>
</span></span></code></pre></div><p>If a function only reads data, use <code>Mapping</code>. If it mutates data, use
<code>MutableMapping</code>. This is a small but important signal for readers and
type checkers.</p>

<h3 id="namedtuple">
  <a class="link" href="#namedtuple">
    ##
  </a>
  NamedTuple
</h3>

<p>When you need a fixed set of fields while keeping tuple behavior,
<code>NamedTuple</code> is useful. It is immutable and indexable, but also lets you
access fields by name.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">NamedTuple</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span><span class="p">(</span><span class="n">NamedTuple</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="nb">id</span><span class="p">:</span> <span class="nb">int</span>
</span></span><span class="line"><span class="cl">    <span class="n">username</span><span class="p">:</span> <span class="nb">str</span>
</span></span><span class="line"><span class="cl">    <span class="n">score</span><span class="p">:</span> <span class="nb">int</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">print_user</span><span class="p">(</span><span class="n">user</span><span class="p">:</span> <span class="n">User</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="n">user</span><span class="o">.</span><span class="n">username</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="n">user</span><span class="o">.</span><span class="n">id</span><span class="si">}</span><span class="s2">) = </span><span class="si">{</span><span class="n">user</span><span class="o">.</span><span class="n">score</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span></code></pre></div><p><code>NamedTuple</code> is good for compact data structures that should not change
after creation. If you need mutability and richer behavior, prefer
<code>dataclass</code> or a regular class.</p>

<h3 id="typeddict">
  <a class="link" href="#typeddict">
    ##
  </a>
  TypedDict
</h3>

<p>If your structure is a dictionary and you want typed keys and values,
use <code>TypedDict</code>. It describes dictionary shape and works at static
analysis level.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">TypedDict</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="nb">id</span><span class="p">:</span> <span class="nb">int</span>
</span></span><span class="line"><span class="cl">    <span class="n">username</span><span class="p">:</span> <span class="nb">str</span>
</span></span><span class="line"><span class="cl">    <span class="n">email</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">send_email</span><span class="p">(</span><span class="n">user</span><span class="p">:</span> <span class="n">User</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span></code></pre></div><p>This is especially useful when data comes from JSON or another dynamic
source but you still want a strict key contract. If some keys are
optional, define that explicitly to keep type checks meaningful.</p>

<h3 id="dataclass">
  <a class="link" href="#dataclass">
    ##
  </a>
  Dataclass
</h3>

<p><code>dataclass</code> is a convenient way to define a data container with
initializer, comparisons, and readable <code>repr</code>. This class is mutable by
default and works well for domain objects and DTOs.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">dataclasses</span> <span class="kn">import</span> <span class="n">dataclass</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@dataclass</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="nb">id</span><span class="p">:</span> <span class="nb">int</span>
</span></span><span class="line"><span class="cl">    <span class="n">username</span><span class="p">:</span> <span class="nb">str</span>
</span></span><span class="line"><span class="cl">    <span class="n">email</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">normalize</span><span class="p">(</span><span class="n">user</span><span class="p">:</span> <span class="n">User</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">User</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span><span class="o">.</span><span class="n">username</span> <span class="o">=</span> <span class="n">user</span><span class="o">.</span><span class="n">username</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">user</span>
</span></span></code></pre></div><p><code>dataclass</code> is a good fit when you need mutable structure and clear data
modeling. If an object must be immutable, use
<code>@dataclass(frozen=True)</code>.</p>

<h3 id="enum">
  <a class="link" href="#enum">
    ##
  </a>
  Enum
</h3>

<p><code>Enum</code> helps define a closed set of values. This is useful when a field
has a strict list of allowed options and you do not want random strings
in your code.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">enum</span> <span class="kn">import</span> <span class="n">Enum</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Status</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">NEW</span> <span class="o">=</span> <span class="s2">&#34;new&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">DONE</span> <span class="o">=</span> <span class="s2">&#34;done&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">FAILED</span> <span class="o">=</span> <span class="s2">&#34;failed&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">is_done</span><span class="p">(</span><span class="n">status</span><span class="p">:</span> <span class="n">Status</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">bool</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">status</span> <span class="ow">is</span> <span class="n">Status</span><span class="o">.</span><span class="n">DONE</span>
</span></span></code></pre></div><p>This approach is practical for statuses, roles, flags, modes, and other
enumerable domain values.</p>

<h3 id="custom-classes">
  <a class="link" href="#custom-classes">
    ##
  </a>
  Custom Classes
</h3>

<p>The type system can specify not only primitives but also your own or
library classes. For example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="nb">id</span><span class="p">:</span> <span class="nb">int</span>
</span></span><span class="line"><span class="cl">    <span class="n">username</span><span class="p">:</span> <span class="nb">str</span>
</span></span><span class="line"><span class="cl">    <span class="n">email</span><span class="p">:</span> <span class="nb">str</span>
</span></span><span class="line"><span class="cl">    <span class="n">friends</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">User</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">hand_shake</span><span class="p">(</span><span class="n">user1</span><span class="p">:</span> <span class="n">User</span><span class="p">,</span> <span class="n">user2</span><span class="p">:</span> <span class="n">User</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">user1</span><span class="o">.</span><span class="n">friends</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">user2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">user2</span><span class="o">.</span><span class="n">friends</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">user1</span><span class="p">)</span>
</span></span></code></pre></div>
<h3 id="abstract-classes">
  <a class="link" href="#abstract-classes">
    ##
  </a>
  Abstract Classes
</h3>

<p>Python has an excellent module, <code>collections.abc</code>. It already defines a
large set of abstract classes that cover most practical needs. They are
useful when you want to describe behavior, not a concrete
implementation. What is available there?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">collections</span><span class="o">.</span><span class="n">abc</span><span class="o">.</span><span class="n">ABCMeta</span>
</span></span><span class="line"><span class="cl"><span class="n">collections</span><span class="o">.</span><span class="n">abc</span><span class="o">.</span><span class="n">AsyncGenerator</span>
</span></span><span class="line"><span class="cl"><span class="n">collections</span><span class="o">.</span><span class="n">abc</span><span class="o">.</span><span class="n">AsyncIterable</span>
</span></span><span class="line"><span class="cl"><span class="n">collections</span><span class="o">.</span><span class="n">abc</span><span class="o">.</span><span class="n">AsyncIterator</span>
</span></span><span class="line"><span class="cl"><span class="n">collections</span><span class="o">.</span><span class="n">abc</span><span class="o">.</span><span class="n">Awaitable</span>
</span></span><span class="line"><span class="cl"><span class="n">collections</span><span class="o">.</span><span class="n">abc</span><span class="o">.</span><span class="n">Buffer</span>
</span></span><span class="line"><span class="cl"><span class="n">collections</span><span class="o">.</span><span class="n">abc</span><span class="o">.</span><span class="n">ByteString</span>
</span></span><span class="line"><span class="cl"><span class="n">collections</span><span class="o">.</span><span class="n">abc</span><span class="o">.</span><span class="n">Callable</span>
</span></span><span class="line"><span class="cl"><span class="n">collections</span><span class="o">.</span><span class="n">abc</span><span class="o">.</span><span class="n">Collection</span>
</span></span><span class="line"><span class="cl"><span class="n">collections</span><span class="o">.</span><span class="n">abc</span><span class="o">.</span><span class="n">Container</span>
</span></span><span class="line"><span class="cl"><span class="n">collections</span><span class="o">.</span><span class="n">abc</span><span class="o">.</span><span class="n">Coroutine</span>
</span></span><span class="line"><span class="cl"><span class="n">collections</span><span class="o">.</span><span class="n">abc</span><span class="o">.</span><span class="n">EllipsisType</span>
</span></span><span class="line"><span class="cl"><span class="n">collections</span><span class="o">.</span><span class="n">abc</span><span class="o">.</span><span class="n">FunctionType</span>
</span></span><span class="line"><span class="cl"><span class="n">collections</span><span class="o">.</span><span class="n">abc</span><span class="o">.</span><span class="n">Generator</span>
</span></span><span class="line"><span class="cl"><span class="n">collections</span><span class="o">.</span><span class="n">abc</span><span class="o">.</span><span class="n">GenericAlias</span>
</span></span><span class="line"><span class="cl"><span class="n">collections</span><span class="o">.</span><span class="n">abc</span><span class="o">.</span><span class="n">Hashable</span>
</span></span><span class="line"><span class="cl"><span class="n">collections</span><span class="o">.</span><span class="n">abc</span><span class="o">.</span><span class="n">ItemsView</span>
</span></span><span class="line"><span class="cl"><span class="n">collections</span><span class="o">.</span><span class="n">abc</span><span class="o">.</span><span class="n">Iterable</span>
</span></span><span class="line"><span class="cl"><span class="n">collections</span><span class="o">.</span><span class="n">abc</span><span class="o">.</span><span class="n">Iterator</span>
</span></span><span class="line"><span class="cl"><span class="n">collections</span><span class="o">.</span><span class="n">abc</span><span class="o">.</span><span class="n">KeysView</span>
</span></span><span class="line"><span class="cl"><span class="n">collections</span><span class="o">.</span><span class="n">abc</span><span class="o">.</span><span class="n">Mapping</span>
</span></span><span class="line"><span class="cl"><span class="n">collections</span><span class="o">.</span><span class="n">abc</span><span class="o">.</span><span class="n">MappingView</span>
</span></span><span class="line"><span class="cl"><span class="n">collections</span><span class="o">.</span><span class="n">abc</span><span class="o">.</span><span class="n">MutableMapping</span>
</span></span><span class="line"><span class="cl"><span class="n">collections</span><span class="o">.</span><span class="n">abc</span><span class="o">.</span><span class="n">MutableSequence</span>
</span></span><span class="line"><span class="cl"><span class="n">collections</span><span class="o">.</span><span class="n">abc</span><span class="o">.</span><span class="n">MutableSet</span>
</span></span><span class="line"><span class="cl"><span class="n">collections</span><span class="o">.</span><span class="n">abc</span><span class="o">.</span><span class="n">Reversible</span>
</span></span><span class="line"><span class="cl"><span class="n">collections</span><span class="o">.</span><span class="n">abc</span><span class="o">.</span><span class="n">Sequence</span>
</span></span><span class="line"><span class="cl"><span class="n">collections</span><span class="o">.</span><span class="n">abc</span><span class="o">.</span><span class="n">Set</span>
</span></span><span class="line"><span class="cl"><span class="n">collections</span><span class="o">.</span><span class="n">abc</span><span class="o">.</span><span class="n">Sized</span>
</span></span><span class="line"><span class="cl"><span class="n">collections</span><span class="o">.</span><span class="n">abc</span><span class="o">.</span><span class="n">ValuesView</span>
</span></span></code></pre></div><p>As you can see, there are many abstract classes. Most describe a
property of a collection. Using them in annotations lets you express
wider polymorphic input boundaries for your functions:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">collections.abc</span> <span class="kn">import</span> <span class="n">Iterable</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">total</span><span class="p">(</span><span class="n">values</span><span class="p">:</span> <span class="n">Iterable</span><span class="p">[</span><span class="nb">int</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">sum</span><span class="p">(</span><span class="n">values</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">total</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="n">total</span><span class="p">((</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="n">total</span><span class="p">({</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">})</span>
</span></span></code></pre></div><p>Sometimes you still see imports from <code>typing</code> like
<code>from typing import Iterable, Sequence</code>. In practice, those are
re-exports from <code>collections.abc</code>. Today it is better to import these
ABCs directly from <code>collections.abc</code>.</p>

<h3 id="sequence-and-iterable">
  <a class="link" href="#sequence-and-iterable">
    ##
  </a>
  Sequence and Iterable
</h3>

<p>These two are often confused. <code>Iterable</code> only guarantees that an object
can be iterated in a loop. No indexing, length, or ordering is
promised. <code>Sequence</code> guarantees iteration plus indexing and length, that
is <code>__getitem__</code> and <code>__len__</code>. This leads to a practical difference in
what operations are safe.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">collections.abc</span> <span class="kn">import</span> <span class="n">Iterable</span><span class="p">,</span> <span class="n">Sequence</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">sum_any</span><span class="p">(</span><span class="n">values</span><span class="p">:</span> <span class="n">Iterable</span><span class="p">[</span><span class="nb">int</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">total</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">total</span> <span class="o">+=</span> <span class="n">v</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">total</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">head</span><span class="p">(</span><span class="n">values</span><span class="p">:</span> <span class="n">Sequence</span><span class="p">[</span><span class="nb">int</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">values</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
</span></span></code></pre></div><p><code>sum_any</code> accepts a list, tuple, or generator. <code>head</code> cannot accept a
generator, because it has no indexing and you cannot do <code>values[0]</code>.
So, if you only need iteration, use <code>Iterable</code>; if you rely on indexing
or length, use <code>Sequence</code>.</p>

<h3 id="callable">
  <a class="link" href="#callable">
    ##
  </a>
  Callable
</h3>

<p><code>Callable</code> is used when a function accepts another function. This is
especially important for callbacks, event handlers, and higher-order
functions.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">collections.abc</span> <span class="kn">import</span> <span class="n">Callable</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">apply</span><span class="p">(</span><span class="n">values</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">],</span> <span class="n">fn</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[[</span><span class="nb">int</span><span class="p">],</span> <span class="nb">int</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">[</span><span class="n">fn</span><span class="p">(</span><span class="n">v</span><span class="p">)</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
</span></span></code></pre></div><p>If the signature is unknown in advance, you can use
<code>Callable[..., ReturnType]</code>, but this should be a last resort.</p>

<h3 id="generics">
  <a class="link" href="#generics">
    ##
  </a>
  Generics
</h3>

<p>Generics are parameterized types. In simple terms, these are types that
accept other types. This matters when you want to preserve the relation
between input and output instead of losing it to <code>Any</code>. For example,
<code>first</code> returns the same item type as inside the input collection:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">collections.abc</span> <span class="kn">import</span> <span class="n">Sequence</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">TypeVar</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">T</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s2">&#34;T&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">first</span><span class="p">(</span><span class="n">items</span><span class="p">:</span> <span class="n">Sequence</span><span class="p">[</span><span class="n">T</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="n">T</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">items</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
</span></span></code></pre></div><p>Without generics, you would write <code>Sequence[Any]</code> and lose output type.
With generics, the analyzer knows that if input is <code>Sequence[str]</code>, the
result is <code>str</code>. This is especially important for collections, factories,
and repositories where one implementation handles multiple types.</p>
<p>It is also useful to know that <code>TypeVar</code> has a <code>bound</code> parameter that
restricts which types can be used in a generic:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">collections.abc</span> <span class="kn">import</span> <span class="n">Hashable</span><span class="p">,</span> <span class="n">Iterable</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">TypeVar</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">HashableT</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s2">&#34;HashableT&#34;</span><span class="p">,</span> <span class="n">bound</span><span class="o">=</span><span class="n">Hashable</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">mode</span><span class="p">(</span><span class="n">data</span><span class="p">:</span> <span class="n">Iterable</span><span class="p">[</span><span class="n">HashableT</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="n">HashableT</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span></code></pre></div>
<h3 id="literal">
  <a class="link" href="#literal">
    ##
  </a>
  Literal
</h3>

<p><code>Literal</code> lets you fix exact allowed values, not just a base type. This
is useful when a parameter has a closed set of modes, statuses, or keys.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Literal</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">export_report</span><span class="p">(</span><span class="nb">format</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="s2">&#34;csv&#34;</span><span class="p">,</span> <span class="s2">&#34;json&#34;</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="nb">bytes</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span></code></pre></div><p>The signature above defines the contract clearly: no third format
exists here. This makes APIs clearer and lets type checkers catch typos
such as <code>&quot;jsno&quot;</code> before runtime.</p>

<h3 id="static-analyzers">
  <a class="link" href="#static-analyzers">
    ##
  </a>
  Static Analyzers
</h3>

<p>Now it is time to discuss what makes typing practical in Python:
static analyzers. They read annotations, compare them with actual code,
and report problems before runtime.</p>
<ul>
<li><code>mypy</code> is the classic static type checker for gradual typing.
Strength: plugin ecosystem and precise per-module strictness tuning.
Weakness: without tuning it can be either too soft or too noisy.</li>
<li><code>pyright</code> is a fast and clear checker.
Strength: good diagnostics and fast feedback.
Weakness: plugin-style extensibility is weaker than in <code>mypy</code>.</li>
<li><code>pyrefly</code> is a new fast Rust analyzer.
Strength: high speed and LSP integration.
Weakness: still young, so some behavior may change.</li>
<li><code>ty</code> is a new Rust tool by Astral (currently beta).
Strength: speed and modern architecture.
Weakness: pre-release stage; feature set is still catching up to
mature checkers.</li>
</ul>
<p>You can install and run them with <code>uv</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">uv tool install mypy
</span></span><span class="line"><span class="cl">uv tool install pyright
</span></span><span class="line"><span class="cl">uv tool install pyrefly
</span></span><span class="line"><span class="cl">uv tool install ty
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">uvx mypy .
</span></span><span class="line"><span class="cl">uvx pyright .
</span></span><span class="line"><span class="cl">uvx pyrefly check
</span></span><span class="line"><span class="cl">uvx ty check
</span></span></code></pre></div><p>Here is a business-oriented example with intentional typing mistakes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">dataclasses</span> <span class="kn">import</span> <span class="n">dataclass</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">NewType</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">UserId</span> <span class="o">=</span> <span class="n">NewType</span><span class="p">(</span><span class="s2">&#34;UserId&#34;</span><span class="p">,</span> <span class="nb">int</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@dataclass</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="nb">id</span><span class="p">:</span> <span class="n">UserId</span>
</span></span><span class="line"><span class="cl">    <span class="n">email</span><span class="p">:</span> <span class="nb">str</span>
</span></span><span class="line"><span class="cl">    <span class="n">is_active</span><span class="p">:</span> <span class="nb">bool</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">discount</span><span class="p">(</span><span class="n">total</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">percent</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">total</span> <span class="o">-</span> <span class="n">total</span> <span class="o">*</span> <span class="p">(</span><span class="n">percent</span> <span class="o">/</span> <span class="mi">100</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">send_invoice</span><span class="p">(</span><span class="n">user</span><span class="p">:</span> <span class="n">User</span><span class="p">,</span> <span class="n">amount</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="ow">not</span> <span class="n">user</span><span class="o">.</span><span class="n">is_active</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="sa">f</span><span class="s2">&#34;invoice for </span><span class="si">{</span><span class="n">user</span><span class="o">.</span><span class="n">email</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">amount</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">main</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span> <span class="o">=</span> <span class="n">User</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="mi">42</span><span class="p">,</span> <span class="n">email</span><span class="o">=</span><span class="mi">123</span><span class="p">,</span> <span class="n">is_active</span><span class="o">=</span><span class="s2">&#34;yes&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">total</span> <span class="o">=</span> <span class="n">discount</span><span class="p">(</span><span class="s2">&#34;1000&#34;</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">send_invoice</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="s2">&#34;500&#34;</span><span class="p">)</span>
</span></span></code></pre></div><p>Running <code>pyright</code> on this file will produce messages roughly like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plaintext" data-lang="plaintext"><span class="line"><span class="cl">error: Type &#34;float&#34; is not assignable to return type &#34;int&#34;
</span></span><span class="line"><span class="cl">error: Type &#34;None&#34; is not assignable to return type &#34;str&#34;
</span></span><span class="line"><span class="cl">error: &#34;Literal[42]&#34; is not assignable to &#34;UserId&#34;
</span></span><span class="line"><span class="cl">error: &#34;Literal[123]&#34; is not assignable to &#34;str&#34;
</span></span><span class="line"><span class="cl">error: &#34;Literal[&#39;yes&#39;]&#34; is not assignable to &#34;bool&#34;
</span></span><span class="line"><span class="cl">error: &#34;Literal[&#39;1000&#39;]&#34; is not assignable to &#34;int&#34;
</span></span><span class="line"><span class="cl">error: &#34;Literal[&#39;500&#39;]&#34; is not assignable to &#34;int&#34;
</span></span></code></pre></div><p>What matters in this output:</p>
<ul>
<li>Errors in <code>discount</code> and <code>send_invoice</code> show function contract
violations: the signature promises one thing, implementation does
another.</li>
<li>Errors in <code>main</code> show boundary-layer issues (input/DTO) passing
wrong types into domain logic.</li>
<li>The <code>UserId</code> error demonstrates why <code>NewType</code> is useful in business
code: a user identifier cannot be accidentally replaced by a plain
<code>int</code> without an explicit decision.</li>
</ul>

<h3 id="stub-files-pyi">
  <a class="link" href="#stub-files-pyi">
    ##
  </a>
  Stub Files (.pyi)
</h3>

<p>Sometimes you want typing for code you cannot or should not edit.
Examples: generated code, third-party library code, or your own module
where you do not want to mix typing and implementation. That is what
stub files with <code>.pyi</code> extension are for.</p>
<p>A <code>.pyi</code> file contains type signatures only and no implementation.
Static analyzers look for these files near source code or in dedicated
<code>types-*</code> packages. Example:</p>
<p><code>calc.py</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">a</span> <span class="o">+</span> <span class="n">b</span>
</span></span></code></pre></div><p><code>calc.pyi</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="n">a</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">b</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span> <span class="o">...</span>
</span></span></code></pre></div><p>This way you can keep typing separate from implementation, sometimes
even without access to source code.</p>

<h3 id="typealias">
  <a class="link" href="#typealias">
    ##
  </a>
  TypeAlias
</h3>

<p>When a type gets complex, move it into an alias to keep signatures
readable. This is especially useful for business terms like <code>UserId</code>,
<code>Currency</code>, or <code>Payload</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">TypeAlias</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">UserId</span><span class="p">:</span> <span class="n">TypeAlias</span> <span class="o">=</span> <span class="nb">int</span>
</span></span><span class="line"><span class="cl"><span class="n">Payload</span><span class="p">:</span> <span class="n">TypeAlias</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">str</span> <span class="o">|</span> <span class="nb">int</span> <span class="o">|</span> <span class="nb">float</span><span class="p">]</span>
</span></span></code></pre></div><p>Then you can write:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">send</span><span class="p">(</span><span class="n">user_id</span><span class="p">:</span> <span class="n">UserId</span><span class="p">,</span> <span class="n">payload</span><span class="p">:</span> <span class="n">Payload</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span></code></pre></div><p>In Python 3.12+, you can use the <code>type</code> statement instead:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="nb">type</span> <span class="n">UserId</span> <span class="o">=</span> <span class="nb">int</span>
</span></span><span class="line"><span class="cl"><span class="nb">type</span> <span class="n">Payload</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">str</span> <span class="o">|</span> <span class="nb">int</span> <span class="o">|</span> <span class="nb">float</span><span class="p">]</span>
</span></span></code></pre></div><p>This is the same alias, just shorter and easier to read.</p>

<h3 id="typealias-vs-newtype">
  <a class="link" href="#typealias-vs-newtype">
    ##
  </a>
  TypeAlias vs NewType
</h3>

<p><code>TypeAlias</code> is just a type synonym, it does not create a new type.
<code>NewType</code> creates a distinct type at static-checking level, even though
at runtime it is just a wrapper function. This is useful when you have
logically different values of the same base type, for example <code>UserId</code>
and <code>OrderId</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">NewType</span><span class="p">,</span> <span class="n">TypeAlias</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">UserId</span><span class="p">:</span> <span class="n">TypeAlias</span> <span class="o">=</span> <span class="nb">int</span>
</span></span><span class="line"><span class="cl"><span class="n">OrderId</span> <span class="o">=</span> <span class="n">NewType</span><span class="p">(</span><span class="s2">&#34;OrderId&#34;</span><span class="p">,</span> <span class="nb">int</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_user</span><span class="p">(</span><span class="n">user_id</span><span class="p">:</span> <span class="n">UserId</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_order</span><span class="p">(</span><span class="n">order_id</span><span class="p">:</span> <span class="n">OrderId</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span></code></pre></div><p><code>UserId</code> and <code>int</code> are treated as the same type. <code>OrderId</code> is not
compatible with <code>int</code> without explicit conversion.</p>

<h2 id="how-to-use-typing-correctly">
  <a class="link" href="#how-to-use-typing-correctly">
    #
  </a>
  How to Use Typing Correctly
</h2>

<p>Set the right expectation first. Like tests, the purpose of types is to
<strong>fail</strong> when the code is wrong. If they never fail, they are not doing
useful work. The less forgiving your typing setup is, the better it
protects your code. Strict typing (<em>like strict tests</em>) catches bugs,
but only if checks can actually fail.</p>
<p>Writing truly correct typed code is hard. It is a separate skill that
grows the same way architecture and testing skills grow. So start small
and increase strictness gradually.</p>

<h3 id="returning-none">
  <a class="link" href="#returning-none">
    ##
  </a>
  Returning <code>None</code>
</h3>

<p>Also consider the case where a function returns nothing:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">print_weather</span><span class="p">(</span><span class="n">weather</span><span class="p">:</span> <span class="n">Weather</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Weather:&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">date</span><span class="p">,</span> <span class="n">data</span> <span class="ow">in</span> <span class="n">weather</span><span class="o">.</span><span class="n">by_days</span><span class="p">()</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="n">date</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;</span><span class="se">\t</span><span class="si">{</span><span class="n">data</span><span class="o">.</span><span class="n">temperature</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;</span><span class="se">\t</span><span class="si">{</span><span class="n">data</span><span class="o">.</span><span class="n">humidity</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;</span><span class="se">\t</span><span class="si">{</span><span class="n">data</span><span class="o">.</span><span class="n">wind_speed</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;========&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">tmp</span> <span class="o">=</span> <span class="n">print_weather</span><span class="p">(</span><span class="n">Weather</span><span class="p">())</span> <span class="o">+</span> <span class="mi">1</span>
</span></span></code></pre></div><p>In Python, if a function has no <code>return</code>, it returns <code>None</code>. Static
analyzers know this too. For example, <code>pyright</code> on this intentionally
broken code will show:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plaintext" data-lang="plaintext"><span class="line"><span class="cl">error:
</span></span><span class="line"><span class="cl">    Operator &#34;+&#34; not supported for types &#34;None&#34; and &#34;Literal[1]&#34;
</span></span></code></pre></div><p>This shows that <code>pyright</code> understands the return type as <code>None</code>. Does
that mean you can skip return annotations? No. First,
<a href="https://peps.python.org/pep-0020/">PEP 20</a> says explicit is better
than implicit. Second, consider developer experience (DX) in Python. Everyone
knows typing is optional, so missing return annotation is ambiguous.
When you see a function signature without return type, you cannot tell
whether the author skipped typing or the function really returns <code>None</code>.
You resolve that only by reading implementation details, which slows
code navigation.</p>

<h3 id="function-input-vs-output">
  <a class="link" href="#function-input-vs-output">
    ##
  </a>
  Function Input vs Output
</h3>

<p>Continuing the return-type topic: avoid using abstract classes from
<code>collections.abc</code>, or custom abstract classes, for function return
types. They are better suited for input types where you want broad
polymorphism. Return values should be concrete so it is clear how to
use the result.</p>





<blockquote class="markdown-alert markdown-alert--important">
  <div class="markdown-alert__title">
    
      <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v9.5A1.75 1.75 0 0 1 14.25 13H8.06l-2.573 2.573A1.458 1.458 0 0 1 3 14.543V13H1.75A1.75 1.75 0 0 1 0 11.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm7 2.25v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"/></svg>
    

    
      Important
    
  </div>

  <p>A function should be more explicit about what concrete type it
returns than what it accepts.</p>
</blockquote>
<br>
<hr>
<br>
<p>If you want to go deeper, here are useful resources to configure your
tools and understand Python typing in detail:</p>
<ul>
<li><a href="https://fastapi.tiangolo.com/python-types/#type-hints-with-metadata-annotations">FastAPI type hints guide (helpful for web
developers)</a></li>
<li><a href="https://realpython.com/python-type-checking/">RealPython, Type Checking
Guide</a></li>
<li><a href="https://github.com/laike9m/Python-Type-Challenges">Exercises</a></li>
</ul>
]]></content:encoded><category>python</category><category>eosp</category></item><item><title>How to contribute</title><link>https://alchemmist.xyz/articles/how-we-contribute/</link><pubDate>Wed, 11 Feb 2026 12:21:00 +0300</pubDate><dc:creator>alchemmist</dc:creator><guid>https://alchemmist.xyz/articles/how-we-contribute/</guid><description>This is a short beginner’s guide on how to work with open source within GitHub flow practices. All examples will use the GitHub platform, but other repository hosting services that provide the necessary functionality to implement GitHub flow can also be used. Here you can find a classic way to contribute to almost any project, as well as learn a few tips and hacks.
So, where does any contribution begin? Ideally, with a problem. A problem can be called anything, including a lack of new features, missing or incomplete documentation pages on a specific topic, some question, or simply trivial bugs that exist everywhere. You personally will not confuse a problem with anything else, because it will be accompanied by an itchy feeling that “something is annoying.” But will this be a problem for other users and developers? In order to understand this and synchronize your perception with the team’s perception, an issue exists.</description><content:encoded><![CDATA[<p>This is a short beginner’s guide on how to work with open source
within GitHub flow practices. All examples will use the GitHub
platform, but other repository hosting services that provide the
necessary functionality to implement GitHub flow can also be
used. Here you can find a classic way to contribute to almost any
project, as well as learn a few tips and hacks.</p>
<p>So, where does any contribution begin? Ideally, with a problem.
A problem can be called anything, including a lack of new
features, missing or incomplete documentation pages on a specific
topic, some question, or simply trivial bugs that exist
everywhere. You personally will not confuse a problem with
anything else, because it will be accompanied by an itchy feeling
that “something is annoying.” But will this be a problem for
other users and developers? In order to understand this and
synchronize your perception with the team’s perception, an issue
exists.</p>





<blockquote class="markdown-alert markdown-alert--caution">
  <div class="markdown-alert__title">
    
      <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M4.47.22A.749.749 0 0 1 5 0h6c.199 0 .389.079.53.22l4.25 4.25c.141.14.22.331.22.53v6a.749.749 0 0 1-.22.53l-4.25 4.25A.749.749 0 0 1 11 16H5a.749.749 0 0 1-.53-.22L.22 11.53A.749.749 0 0 1 0 11V5c0-.199.079-.389.22-.53Zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5ZM8 4a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 4Zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"/></svg>
    

    
      CONTRIBUTION.md
    
  </div>

  <p>Every project has its own established rules on how to
participate in it. These rules are described either in
<code>README.md</code> in a separate “Contribution” paragraph, or in
a special <code>CONTRIBUTION.md</code> file. Be sure to read it at the
very beginning. This file has the highest priority. That is,
even if you understand that some practices deviate from the
correct scheme that we will consider below, you should still
follow them. Some large projects, such as <code>Postgres</code> or
<code>linux-kernel</code>, still work entirely via mailing lists. One
should respect the traditions that have formed in the
repository, even if they now look like legacy.</p>
</blockquote>

<h2 id="1-issue">
  <a class="link" href="#1-issue">
    #
  </a>
  1. Issue
</h2>

<p>An issue is a ticket that is stored in the repository. Anyone who
comes to the repository can create it. But I recommend that
before rushing to describe your problem, you make several
attempts to search for similar issues, so as not to burden the
repository maintainers with unnecessary work. Let’s see how this
can be done:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">gh issue list --search <span class="s2">&#34;app crashed&#34;</span>
</span></span></code></pre></div><blockquote class="markdown-blockquote">
  <p>This and all further examples will use the official <code>gh</code>
utility. It allows you to perform all basic GitHub operations
from the console. I recommend using it for simple operations to
speed up your workflow.</p>

</blockquote>
<p>The <code>search</code> parameter allows you to add various search filters
directly inside it. Let’s try to add them:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">gh issue list --search <span class="s2">&#34;app crashed is:open label:bug created:&gt;2024-01-01&#34;</span>
</span></span></code></pre></div><p>It is quite possible, especially in large projects, that this
will allow you to find an already created ticket for your problem
and even find a solution or answer in it. If not, then do not
hesitate to open a new issue. The following scheme will help
structure it in the best way:</p>
<pre class="mermaid">
  graph LR
    A[Excepted] ---&gt; B(&#34;Action (run/click/etc)&#34;)
    B ---&gt; C[Got]
</pre>

<p>In large projects, there are issue templates that will help you
format an issue according to the standards adopted in that
project. You can create an issue with the command:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">gh issue create
</span></span></code></pre></div><p>Another excellent practice is providing a Docker image in which
the entire environment is configured and the problem is
guaranteed to be reproducible. Or at least provide a log.
A professional approach would also be to localize the problem as
much as possible. For example, you received an error in the
following file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">fruits</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">logistic</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">a</span> <span class="o">=</span> <span class="n">fruits</span><span class="o">.</span><span class="n">Apple</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">basket</span> <span class="o">=</span> <span class="n">fruits</span><span class="o">.</span><span class="n">MakeBasket</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">basket</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">logistic</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="n">basket</span><span class="p">)</span>
</span></span></code></pre></div><p>Of course, you could write exactly this in the issue. But it
would be even better to independently experiment with this code
and understand what exactly is wrong. For example, check whether
the apple was successfully added to the basket:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">basket</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">basket</span><span class="o">.</span><span class="n">content</span><span class="p">())</span>
</span></span></code></pre></div><p>Perhaps the problem is that we are trying to send an empty
basket, which would mean a bug in the <code>put</code> method. Or perhaps
the problem is in the <code>send</code> method after all. In this way, we
cut off the unnecessary, leaving the essence of the problem in
the issue message.</p>
<p>Remember that even the fact of successfully creating an issue—for
example, proposing a valuable feature or discovering a real bug
or vulnerability—is already a contribution to the project, and
that is already a success. A failure, on the other hand, would be
creating a duplicate issue or overloading maintainers with
unnecessary context.</p>

<h2 id="2-fork">
  <a class="link" href="#2-fork">
    #
  </a>
  2. Fork
</h2>

<p>When you understand which problem you are solving and are
convinced that it is really a problem, you can move on to solving
it. To do this, you should first fork the repository. This will
be valuable support for you if, for example, you are testing CI
or simply getting far ahead of the original repository, and it
will also allow you to send pull requests, since you most likely
do not have permission to make changes to the original
repository:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">gh repo fork &lt;OWNER&gt;/&lt;REPO&gt;
</span></span></code></pre></div><p>After cloning the fork locally, make changes in a separate
branch, which it will be more convenient to name after the issue
number you are trying to close. Why? Because in a short, concise
branch name it is <strong>far</strong> from always possible to express the
essence of the problem, which may have taken several months of
discussion in the ticket. Therefore, it is much more convenient
to simply refer to it by the issue number:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">git switch -c <span class="m">42</span>
</span></span></code></pre></div><p>In GitHub flow, we do not create a complex branch structure where
each branch has its own meaning. Instead, we have a master branch
into which only changes that have passed all checks are merged.
These changes come there from many so-called feature branches,
which we propose to tie to specific issues. And from time to
time, we release a version from the master branch. As a result,
we get a fairly concise structure:</p>
<pre class="mermaid">
  %%{init: {
  &#34;gitGraph&#34;: {
    &#34;tagLabelColor&#34;: &#34;#ffffff&#34;,
    &#34;tagLabelBackground&#34;: &#34;#d73a49&#34;
  }
}}%%
gitGraph
    commit
    commit
    branch &#34;42&#34;
    checkout &#34;42&#34;
    commit
    commit
    checkout main
    merge &#34;42&#34;
    commit tag: &#34;🚩 v1.1.23&#34;

    branch &#34;44&#34;
    checkout &#34;44&#34;
    commit
    commit
    commit
    checkout main
    merge &#34;44&#34;
    commit tag: &#34;🚩 v1.2.0&#34;
</pre>

<p>Also, do not forget to synchronize your <code>fork</code> so as not to lose
connection with the original project:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">gh repo sync &lt;your-username&gt;/&lt;fork-repo&gt; --branch main
</span></span></code></pre></div>
<h2 id="3-pull-request">
  <a class="link" href="#3-pull-request">
    #
  </a>
  3. Pull request
</h2>

<p>When the changes are made and committed, you can send a pull
request. The most convenient way is to simply stay on your branch
and run the command below. <code>gh</code> will form a suitable pull request
itself:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">gh pr create
</span></span></code></pre></div><p>After creating the PR, checks may automatically start if CI is
configured in the repository. And this is great. Because checks
simplify and speed up the review process. You can immediately
see, for example, that your code does not pass style checks, or
that you did not write tests for your code and because of this
the code coverage failed. Do not ignore this, but make new
commits in the same branch, and after pushing them they will
automatically be added to the PR and the checks will be
restarted. The repository maintainer will not even begin
reviewing your PR until all checks in it turn green. You can
check the status of the pull request with the command:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">gh pr status
</span></span></code></pre></div><p>Also try to make a neat commit history in the pull request to
simplify the review process.
<a href="https://www.geeksforgeeks.org/git/git-squash/">Squash</a> commits
will help you with this.</p>
<p>When you have broken through all automated checks, do not
hesitate to contact the repository architect and ask for
a review. He will either review your PR himself or delegate it to
someone from the team. On GitHub, this is done simply via a tag:
<code>@username</code>. Often, notifications that a new pull request has
arrived may be disabled, and moreover, the architect will not
know when you have made all the fixes to pass CI. Therefore,
modesty here may lead to the fact that your pull request will
simply not be seen by anyone. This, by the way, also applies to
issues.</p>
<p>If you understand that your pull request completely closes the
issue you were solving, then in the PR messages you can specify
<code>Close #49</code>, and then the issue will be automatically closed
after the pull request is merged.</p>

<h2 id="4-merge">
  <a class="link" href="#4-merge">
    #
  </a>
  4. Merge
</h2>

<p>So, when you have passed all automated checks and manual review,
your code will get into the master branch of the repository, and
you can consider yourself a full-fledged contributor to this
project. Keep it up!</p>
<p>Also pay attention to an interesting practice that, in my
opinion, is very professional. Imagine that you discovered and
reported some bug in an issue. This is already a contribution to
the repository. Next, it is proposed to create a pull request in
which you write a test that reproduces this bug. The test, of
course, will fail. But you send the pull request, thereby adding
this test in a disabled state to the codebase. You can also leave
a <code>TODO</code> tag. And this will become an even greater contribution
to the repository, since you have added a meaningful test, and
this is always very useful. And finally, with a second pull
request, you already submit changes that fix the code so that it
passes this test. And once again you increase your contribution.
Thus, you have left behind a lot of artifacts in the repository:
an issue, an enhanced test system, and a fixed bug. This will be
an excellent example of implementing the TDD (Test Driven
Development) approach.</p>
<br>
<hr>
<br>
<p>Thus, you and I have gone through the full cycle from the problem to the contribution
to the master branch.:</p>
<pre class="mermaid">
  flowchart LR
    BUG[&#34;Find a bug / feature   &#34;] --&gt; ISSUE[&#34;Create Issue  &#34;]
    ISSUE --&gt; DISCUSS[&#34;Discussion  &#34;]
    DISCUSS --&gt; PR[&#34;Open Pull Request  &#34;]
    PR --&gt; CI[&#34;PR Checks with CI 󰦕 &#34;]
    CI --&gt; REVIEW[&#34;Reviewer Reviews PR  &#34;]
    REVIEW --&gt; MERGE[&#34;Merge PR into main  &#34;]
    MERGE --&gt; BUG
</pre>

<p>You can test your strength right now by finding a suitable issue
using these wonderful services:</p>
<ul>
<li><a href="https://goodfirstissue.dev/">Good First Issue</a> — Issues that
are great for beginners.</li>
<li><a href="https://github.com/MunGell/awesome-for-beginners">Awesome for
Beginners</a>
— A collection of projects for beginners.</li>
<li><a href="https://up-for-grabs.net/#/">Up For Grabs</a> — Tasks open for
solving across various technologies.</li>
<li><a href="https://github.com/firstcontributions/first-contributions">First
Contributions</a>
— A repository with a manual and a list of open projects.</li>
<li><a href="https://www.firsttimersonly.com/">First Timers Only</a> — More
similar resources.</li>
</ul>
<p>Do not forget that you yourself can also register your projects
on these resources to attract new developers!</p>





<blockquote class="markdown-alert markdown-alert--note">
  <div class="markdown-alert__title">
    
      <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"/></svg>
    

    
      GitHub CLI
    
  </div>

  <p>If you are interested in productive work with GitHub from the
console using the <code>gh</code> utility, pay attention to it
<a href="https://cli.github.com/manual/">documentation</a>. And also for
that, that <code>gh</code> supports extensions that you can familiarize
yourself with you can use the <code>gh ext browse</code> command or write
your own!</p>
</blockquote>
<p>Some additional reading:</p>
<ul>
<li><a href="https://opensource.guide/how-to-contribute/">Official GitHub
guideline</a></li>
<li><a href="https://navendu.me/posts/pull-requests-like-a-pro/">Pull Requests like a Pro, Navendu
Pottekkat</a></li>
<li><a href="https://dev.to/helloquash/10-common-mistakes-to-avoid-when-contributing-to-open-source-projects-1mna">10 Common Mistakes to Avoid When Contributing to Open Source
Projects</a></li>
<li><a href="https://github-help-wanted.com/open-source/beginners-guide/">Open Source for
Beginners</a></li>
</ul>
]]></content:encoded><category>open-source</category><category>eosp</category></item></channel></rss>