<?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>DEV Community: mprytula</title>
    <description>The latest articles on DEV Community by mprytula (@mprytula).</description>
    <link>https://dev.to/mprytula</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%2F1322738%2F84851adc-ef7f-4430-bd05-c773c2c4c6b5.png</url>
      <title>DEV Community: mprytula</title>
      <link>https://dev.to/mprytula</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mprytula"/>
    <language>en</language>
    <item>
      <title>Design by Contract in OOP with TypeScript</title>
      <dc:creator>mprytula</dc:creator>
      <pubDate>Fri, 13 Mar 2026 22:08:45 +0000</pubDate>
      <link>https://dev.to/mprytula/design-by-contract-in-oop-with-typescript-469n</link>
      <guid>https://dev.to/mprytula/design-by-contract-in-oop-with-typescript-469n</guid>
      <description>&lt;h2&gt;
  
  
  What Is a Contract?
&lt;/h2&gt;

&lt;p&gt;A contract is a set of rules that defines how two parties are expected to interact. It answers three questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what is allowed as input;&lt;/li&gt;
&lt;li&gt;what must be produced as output;&lt;/li&gt;
&lt;li&gt;who is responsible for what.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In real life, this is easy to understand through the example of an employment agreement. An employee agrees to work during the agreed hours, and the employer agrees to pay a salary. If both sides follow the terms, the relationship remains predictable. If not, the contract is broken.&lt;/p&gt;

&lt;p&gt;The same idea applies to programming. One module calls another and expects certain behavior. In order for the system to remain reliable, the rules must be clear in advance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what data may be passed in;&lt;/li&gt;
&lt;li&gt;what result should be produced;&lt;/li&gt;
&lt;li&gt;what conditions must always remain true.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is exactly what &lt;strong&gt;Design by Contract&lt;/strong&gt; is about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contracts in Programming
&lt;/h2&gt;

&lt;p&gt;In practice, many bugs do not come from complicated logic, but from implicit assumptions. One module assumes it will receive valid data. Another assumes the method will validate everything on its own. As a result, behavior becomes confusing and unpredictable.&lt;/p&gt;

&lt;p&gt;Consider this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentProcessingService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;moneyTransferProvider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MoneyTransferProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;commissionRate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="c1"&gt;// The rules for using this method are not expressed explicitly&lt;/span&gt;
    &lt;span class="nf"&gt;processPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isUUID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;commission&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commissionRate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;moneyTransferProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;commission&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first glance, this code looks acceptable, but it has a serious problem: the contract of the method is hidden inside the implementation.&lt;/p&gt;

&lt;p&gt;From the method signature alone, it is impossible to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what kind of &lt;code&gt;target&lt;/code&gt; is considered valid;&lt;/li&gt;
&lt;li&gt;whether any string can be passed as &lt;code&gt;currency&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;what the return value means;&lt;/li&gt;
&lt;li&gt;how to distinguish a successful result from an error.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To use such a method safely, the developer has to read its implementation and guess the rules. That is a bad sign: the contract should be visible in the API, not buried in the method body.&lt;/p&gt;

&lt;p&gt;The situation becomes even worse if the system contains several similar services. One may return &lt;code&gt;0&lt;/code&gt; on failure, another may throw an exception, and a third may skip validation entirely. From the outside, the methods look similar, but their actual behavior is different. This makes the system fragile.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Design by Contract Offers
&lt;/h2&gt;

&lt;p&gt;Design by Contract addresses this problem by making the rules explicit. A method should not merely perform some action; it should have a clearly defined contract.&lt;/p&gt;

&lt;p&gt;A contract usually consists of three parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;preconditions&lt;/strong&gt; — what the caller must guarantee before invoking the method;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;postconditions&lt;/strong&gt; — what the method must guarantee after execution;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;invariants&lt;/strong&gt; — properties that must always remain true.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The most well-known implementation of this approach was introduced in the Eiffel programming language. In Eiffel, a contract can be declared directly alongside the method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight eiffel"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt;
    &lt;span class="nc"&gt;PAYMENT_PROCESSING_SERVICE&lt;/span&gt;

&lt;span class="k"&gt;feature&lt;/span&gt; 

    &lt;span class="n"&gt;process_payment&lt;/span&gt; &lt;span class="pi"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;STRING&lt;/span&gt;&lt;span class="pi"&gt;;&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;REAL_64&lt;/span&gt;&lt;span class="pi"&gt;;&lt;/span&gt; &lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;STRING&lt;/span&gt;&lt;span class="pi"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;REAL_64&lt;/span&gt;
        &lt;span class="k"&gt;require&lt;/span&gt;
            &lt;span class="n"&gt;target_attached&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;/=&lt;/span&gt; &lt;span class="k"&gt;Void&lt;/span&gt;
            &lt;span class="n"&gt;target_is_uuid&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="n"&gt;is_uuid&lt;/span&gt; &lt;span class="pi"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;amount_positive&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="mf"&gt;.0&lt;/span&gt;
            &lt;span class="n"&gt;currency_attached&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="n"&gt;currency&lt;/span&gt; &lt;span class="o"&gt;/=&lt;/span&gt; &lt;span class="k"&gt;Void&lt;/span&gt;
            &lt;span class="n"&gt;currency_not_empty&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="pi"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_empty&lt;/span&gt;
            &lt;span class="n"&gt;currency_supported&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="n"&gt;is_supported_currency&lt;/span&gt; &lt;span class="pi"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="pi"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;local&lt;/span&gt;
            &lt;span class="n"&gt;commission&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;REAL_64&lt;/span&gt;
        &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="n"&gt;commission&lt;/span&gt; &lt;span class="pi"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;commission_rate&lt;/span&gt;
            &lt;span class="n"&gt;money_transfer_provider&lt;/span&gt;&lt;span class="pi"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transfer&lt;/span&gt; &lt;span class="pi"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="pi"&gt;)&lt;/span&gt;
            &lt;span class="kv"&gt;Result&lt;/span&gt; &lt;span class="pi"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;commission&lt;/span&gt;
        &lt;span class="k"&gt;ensure&lt;/span&gt;
            &lt;span class="n"&gt;calculated_correctly&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kv"&gt;Result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;commission_rate&lt;/span&gt;
            &lt;span class="n"&gt;result_positive&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kv"&gt;Result&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="mf"&gt;.0&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even without knowing Eiffel, the idea is clear: before the method runs, the required conditions for the input are listed explicitly; after it runs, the guarantees about the result are also stated explicitly.&lt;/p&gt;

&lt;p&gt;This is useful for two reasons.&lt;/p&gt;

&lt;p&gt;First, the caller can immediately see how the method is supposed to be used.&lt;/p&gt;

&lt;p&gt;Second, the implementation cannot behave arbitrarily: it is obligated to fulfill the promises declared in the contract.&lt;/p&gt;

&lt;p&gt;This is especially important in inheritance. If a base class defines a contract, a subclass should not unexpectedly change the rules and require something different from the client.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Implement This in TypeScript
&lt;/h2&gt;

&lt;p&gt;TypeScript does not have built-in contract support like Eiffel. We cannot declare invariants and postconditions directly in a method signature in the same way.&lt;/p&gt;

&lt;p&gt;But that does not make the idea useless. It simply means the contract has to be expressed differently.&lt;/p&gt;

&lt;p&gt;The key principle here is this: &lt;strong&gt;the caller is primarily responsible for satisfying the preconditions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If every method revalidates everything internally, the code quickly turns into a mess of repeated checks. This is often called &lt;em&gt;validation hell&lt;/em&gt;: the same validation logic is scattered across multiple layers of the system, and the boundaries of responsibility become unclear.&lt;/p&gt;

&lt;p&gt;In TypeScript, the main tools for expressing a contract are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;parameter types;&lt;/li&gt;
&lt;li&gt;return types;&lt;/li&gt;
&lt;li&gt;domain objects that do not allow invalid states to be created.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The idea is simple: if a method accepts not just &lt;code&gt;string&lt;/code&gt; and &lt;code&gt;number&lt;/code&gt;, but &lt;code&gt;UuidV4&lt;/code&gt;, &lt;code&gt;Money&lt;/code&gt;, and &lt;code&gt;Percentage&lt;/code&gt;, then part of the contract is already expressed in the signature itself.&lt;/p&gt;

&lt;p&gt;Consider an improved version of the example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentProcessingService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;moneyTransferProvider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MoneyTransferProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;commissionRate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CommissionRate&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="nf"&gt;processPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UuidV4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;money&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Money&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;moneyToTransfer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commissionRate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;money&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;moneyTransferProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;moneyToTransfer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the method is much clearer.&lt;/p&gt;

&lt;p&gt;From the signature alone, we can already see that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;target&lt;/code&gt; must be a valid &lt;code&gt;UuidV4&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;money&lt;/code&gt; is not just a raw number, but an object that already contains a valid amount and a valid currency;&lt;/li&gt;
&lt;li&gt;the service does not need to repeatedly validate raw strings and numbers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words, part of the invariants has been moved out of the method body and into the types.&lt;/p&gt;

&lt;h3&gt;
  
  
  Invariants Inside Domain Types
&lt;/h3&gt;

&lt;p&gt;For example, a valid commission can be represented as a separate type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CommissionRate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Percentage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Percentage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;money&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Money&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Money&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;money&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;CommissionRate&lt;/code&gt; does not check whether the percentage falls within the allowed range. That responsibility already belongs to the &lt;code&gt;Percentage&lt;/code&gt; type. As a result, the service works only with valid values and does not duplicate validation.&lt;/p&gt;

&lt;p&gt;Money can also be modeled as a separate type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;Currency&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Euro&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;euro&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;USD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;usd&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Money&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PositiveNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Currency&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="nf"&gt;withdraw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;money&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Money&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Money&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;money&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currency&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InvalidParameterError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cannot withdraw money of different currencies&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Money&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subtract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;money&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAmount&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currency&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;getAmount&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;PositiveNumber&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;getCurrency&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;Currency&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This kind of type is useful because it keeps the rules for handling money in one place. The system no longer operates on plain numbers. It operates on objects that already define their own constraints and allowed operations.&lt;/p&gt;

&lt;p&gt;The same applies to positive numbers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PositiveNumber&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&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="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InvalidParameterError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Value must be positive&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MAX_SAFE_INTEGER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InvalidParameterError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s2"&gt;`Value must not exceed &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MAX_SAFE_INTEGER&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;subtract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;other&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PositiveNumber&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;PositiveNumber&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PositiveNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;other&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Such a class prevents invalid values from being created in the first place. That means any code receiving a &lt;code&gt;PositiveNumber&lt;/code&gt; can already rely on the basic validity of the data.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Gives Us
&lt;/h2&gt;

&lt;p&gt;This approach makes code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;easier to understand;&lt;/li&gt;
&lt;li&gt;more predictable;&lt;/li&gt;
&lt;li&gt;less dependent on hidden validation logic;&lt;/li&gt;
&lt;li&gt;more resilient at module boundaries.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Validation is concentrated where values are created, instead of being spread across the entire system. Inside business logic, there is less noise and more clarity.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Limits of TypeScript
&lt;/h2&gt;

&lt;p&gt;Of course, TypeScript does not give us the same guarantees as Eiffel.&lt;/p&gt;

&lt;p&gt;We still cannot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fully declare method contracts at the language level;&lt;/li&gt;
&lt;li&gt;strictly enforce postconditions;&lt;/li&gt;
&lt;li&gt;completely protect ourselves from subclasses changing behavior and breaking the base contract.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is why Design by Contract in TypeScript is not a built-in language mechanism, but rather a design discipline.&lt;/p&gt;

&lt;p&gt;Even so, this discipline is still enough to significantly improve code quality. In practice, it helps to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;make domain types as precise as possible;&lt;/li&gt;
&lt;li&gt;avoid inheritance where composition is sufficient;&lt;/li&gt;
&lt;li&gt;cover important contracts with tests;&lt;/li&gt;
&lt;li&gt;document preconditions and invariants clearly in the API and documentation.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;TypeScript cannot implement Design by Contract in its classical form. But it can get close to the same idea through types, value objects, and careful separation of responsibility.&lt;/p&gt;

&lt;p&gt;That is the practical value of the approach: not to validate everything everywhere, but to design the system in such a way that as many invalid states as possible become impossible to represent in the first place.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>programming</category>
      <category>softwareengineering</category>
      <category>typescript</category>
    </item>
    <item>
      <title>A year after...</title>
      <dc:creator>mprytula</dc:creator>
      <pubDate>Sun, 11 Jan 2026 21:00:50 +0000</pubDate>
      <link>https://dev.to/mprytula/a-year-after-4c39</link>
      <guid>https://dev.to/mprytula/a-year-after-4c39</guid>
      <description>&lt;p&gt;When I was looking for a job, I was genuinely frustrated that most companies were not willing to consider developers without experience, even for Junior Developer positions. I couldn’t understand what objective advantages an experienced developer had over a beginner.&lt;/p&gt;

&lt;p&gt;Now, after a year of working as a commercial developer, I want to share how this year has influenced my perspective.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 Soft Skills
&lt;/h2&gt;

&lt;p&gt;Most beginners, including my past self, significantly underestimate the importance of soft skills in modern software development. Meanwhile, they are critically important for any developer: they improve not only relationships within the team, but also personal productivity.&lt;/p&gt;

&lt;p&gt;Effective communication — both with the team and with the business — greatly simplifies requirement clarification and task definition. In my experience, this accounts for around 65% of success in completing any task. Unfortunately, this is one of the skills that is almost impossible to develop without real work experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  🏗️ Architectural Thinking
&lt;/h2&gt;

&lt;p&gt;Architectural thinking is the ability to consider a task not in isolation, but in the context of the entire system. This skill goes far beyond theoretical knowledge of SOLID, GRASP, or other design patterns. It includes understanding a specific product: which parts of the system are critical, where the bottlenecks are, which areas require scalability, and where additional flexibility would only increase the estimate without providing real value ⚖️.&lt;/p&gt;

&lt;p&gt;I believe this type of thinking is primarily formed through working on a specific product and deeply understanding its domain. At the same time, knowledge of general design principles such as SOLID significantly accelerates and simplifies this process.&lt;/p&gt;

&lt;h2&gt;
  
  
  💼 Business-Oriented Thinking
&lt;/h2&gt;

&lt;p&gt;Lack of business-oriented thinking is a common issue among junior developers and beyond.&lt;/p&gt;

&lt;p&gt;By business-oriented thinking, I mean the ability to prioritize company goals over personal preferences — for example, agreeing to speed up the development of a specific feature at the cost of temporarily reducing code quality. This approach often conflicts with internal perfectionism, but it is important to understand that the business does not care &lt;em&gt;how&lt;/em&gt; the code is written. It cares about &lt;em&gt;how&lt;/em&gt; it works and how much it costs to deliver a feature.&lt;/p&gt;

&lt;p&gt;This is especially relevant for startups, where releasing new functionality provides a competitive advantage here and now 🚀. That advantage is directly tied to revenue, which in turn leads to team growth, salary increases, and — in the long term — more time for high-quality technical work.&lt;/p&gt;

&lt;p&gt;At the same time, it is important to maintain a delicate balance. Development interests cannot be ignored. If short-term acceleration through smelly code leads to significant slowdowns in the medium term, it is the developer’s responsibility to warn the business about the consequences. Otherwise, this would conflict with the company’s long-term interests in growth and sustainable development.&lt;/p&gt;




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

&lt;p&gt;A year of commercial development helped me realize that experience is not an innate advantage, but a set of skills and approaches that gradually form through practice. Communication, architectural thinking, and business-oriented thinking all develop over time and do not automatically appear with the first job.&lt;/p&gt;

&lt;p&gt;For developers without experience, this means that the gap is not insurmountable 💪. A conscious focus on these aspects, genuine interest in the product, and a willingness to learn can significantly shorten the distance and help transition faster from a beginner to a full-fledged member of the team.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Macroservices</title>
      <dc:creator>mprytula</dc:creator>
      <pubDate>Sun, 02 Nov 2025 18:27:20 +0000</pubDate>
      <link>https://dev.to/mprytula/macroservices-1emb</link>
      <guid>https://dev.to/mprytula/macroservices-1emb</guid>
      <description>&lt;h2&gt;
  
  
  Essence
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Macro&lt;/em&gt;-services emerge from incorrect microservice decomposition and significantly complicate development. They are oversized microservices responsible for too many processes that could have been separated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Macroservice
&lt;/h2&gt;

&lt;p&gt;A macroservice appears when a single service combines multiple loosely related business processes.&lt;/p&gt;

&lt;p&gt;To identify weak coupling, clearly define what the service actually does — the specific business problem it solves in one sentence. If that sentence contains an &lt;em&gt;“and”&lt;/em&gt;, it’s a clear sign the service handles unrelated processes.&lt;/p&gt;

&lt;p&gt;When these processes are decoupled, a new business model can emerge that differs from the original one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Symptoms
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The model becomes large and difficult to develop&lt;/li&gt;
&lt;li&gt;Several teams work on the same service simultaneously&lt;/li&gt;
&lt;li&gt;Many dependencies on external services&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How to fix it
&lt;/h3&gt;

&lt;p&gt;Define the service’s area of responsibility and design the minimal model needed to function within it.&lt;/p&gt;

&lt;p&gt;Let’s look at a messenger application and its &lt;em&gt;user service.&lt;/em&gt;&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%2Fbfio5w9donsgtqfq4v6v.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%2Fbfio5w9donsgtqfq4v6v.png" alt=" " width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This service manages the user: their chats, contacts, account settings, blacklist, etc.&lt;/p&gt;

&lt;p&gt;But why does managing account settings require knowledge about chats? Why should the blacklist involve chat groups?&lt;/p&gt;

&lt;p&gt;A key problem is the growing complexity of the model — for example, if we want to add a whitelist, split contacts into “registered” and “spontaneous,” or introduce a message deletion timer, the user model would become even heavier.&lt;/p&gt;

&lt;p&gt;That’s how weak coupling becomes visible. Let’s decompose the &lt;em&gt;user service.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Decomposition
&lt;/h3&gt;

&lt;p&gt;I split the services into three, each with its own responsibility:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;user account management&lt;/em&gt; — handles account settings&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;social&lt;/em&gt; — handles interactions between users&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;chat&lt;/em&gt; — handles messaging&lt;/li&gt;
&lt;/ul&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%2Ft2jsck9e6u4k6k40kkk5.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%2Ft2jsck9e6u4k6k40kkk5.png" alt=" " width="792" height="534"&gt;&lt;/a&gt;&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%2Fe4dspya3uz92uozv1a41.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%2Fe4dspya3uz92uozv1a41.png" alt=" " width="578" height="380"&gt;&lt;/a&gt;&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%2F1dqo804qb3q050a1lto2.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%2F1dqo804qb3q050a1lto2.png" alt=" " width="710" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this structure, the processes and dependencies inside each service are easier to understand. By increasing granularity, each team can own a separate service without overlapping in their work.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>microservices</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Low Coupling and High Cohesion: simple with diagrams</title>
      <dc:creator>mprytula</dc:creator>
      <pubDate>Fri, 08 Aug 2025 22:00:05 +0000</pubDate>
      <link>https://dev.to/mprytula/low-coupling-and-high-cohesion-simple-with-diagrams-5hij</link>
      <guid>https://dev.to/mprytula/low-coupling-and-high-cohesion-simple-with-diagrams-5hij</guid>
      <description>&lt;h2&gt;
  
  
  Short description
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Low coupling&lt;/em&gt; and &lt;em&gt;high cohesion&lt;/em&gt; have been around for a long time as part of the GRASP patterns. However, the difference between coupling and cohesion is subtle. Many developers tend to mix them up, or understand only one (usually coupling) but not the other. &lt;br&gt;
Please, keep in mind, that this article reflects my own interpretation of the concepts of &lt;em&gt;cohesion&lt;/em&gt; and &lt;em&gt;coupling&lt;/em&gt; as presented in &lt;em&gt;Clean Code&lt;/em&gt; by Robert C. Martin.&lt;/p&gt;

&lt;p&gt;This note covers following questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What’s the difference between coupling and cohesion?&lt;/li&gt;
&lt;li&gt;When does coupling become cohesion?&lt;/li&gt;
&lt;li&gt;Why can’t coupling exist without cohesion?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Coupling and Cohesion
&lt;/h2&gt;

&lt;p&gt;Coupling and cohesion are closely related concepts. The confusion often comes from the language itself — the words sound similar and are sometimes used interchangeably in everyday conversation.&lt;/p&gt;

&lt;p&gt;Both measure &lt;em&gt;communication&lt;/em&gt; or &lt;em&gt;dependency&lt;/em&gt; between entities. The difference lies in &lt;strong&gt;the level at which the communication happens&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cohesion&lt;/strong&gt;: interaction between elements &lt;em&gt;inside&lt;/em&gt; the same module.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Coupling&lt;/strong&gt;: interaction between &lt;em&gt;different&lt;/em&gt; modules.
&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%2F49v3nf0mzqosuxjxzjhs.png" alt="Coupling and Cohesion" width="800" height="423"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When Does Coupling Become Cohesion?
&lt;/h2&gt;

&lt;p&gt;Remember:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cohesion = communication between elements inside a module
&lt;/li&gt;
&lt;li&gt;Coupling = communication between a module and something outside it
As abstraction level raises, so does the definition of the module. So in the scope of the package, what used to be a coupling in the scope of the classes - becomes cohesion. 
&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%2Fdpbn9xrp5whr6fykgecs.png" alt="Coupling and Cohesion at the higher scope" width="800" height="647"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Low Coupling, High Cohesion
&lt;/h2&gt;

&lt;p&gt;That's why the pattern is called &lt;em&gt;Low Coupling, High Cohesion&lt;/em&gt; because the two are linked — you can’t have one without the other — but coupling introduces risk.&lt;/p&gt;

&lt;p&gt;The goal is to keep &lt;strong&gt;coupling&lt;/strong&gt; as &lt;em&gt;low&lt;/em&gt; as possible. That means minimizing and controlling the points where modules depend on each other(coupling points), while keeping &lt;strong&gt;cohesion&lt;/strong&gt; &lt;em&gt;high&lt;/em&gt; within each module so it’s internally well-organized.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real life Examples:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Low coupling:&lt;/strong&gt; Other departments communicate with IT &lt;em&gt;only&lt;/em&gt; through the CTO. This creates a single, stable point of contact, reducing miscommunication and making the system more resilient. The main risk is if the CTO changes, but that’s a controlled and infrequent event.&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%2Fq3j4b0dszivgn0zlwfwx.png" alt="Low coupling" width="800" height="416"&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High coupling case:&lt;/strong&gt; Other departments talk directly to individual developers. This increases risk and instability because many points of dependency can change frequently. It’s far harder to guarantee stability here.
&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%2Fz55dq4wh4zuq8g3h9rok.png" alt="High coupling" width="800" height="377"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Not Reduce Both?
&lt;/h2&gt;

&lt;p&gt;You might think: &lt;em&gt;If coupling (the bad guy) and cohesion are tied together, why not lower cohesion to reduce coupling?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Because low cohesion breaks the &lt;strong&gt;Single Responsibility Principle&lt;/strong&gt; (SRP). Cohesion between internal elements ensures each part has a clear, minimal responsibility.&lt;/p&gt;

&lt;p&gt;Imagine merging the &lt;strong&gt;Sales&lt;/strong&gt; and &lt;strong&gt;IT&lt;/strong&gt; departments into one hybrid “Sales-IT” team. Technically, there’s no coupling between them anymore — they’re one module. But now the team violates SRP, and the risks of having unrelated responsibilities mixed together outweigh the benefits.&lt;/p&gt;

&lt;p&gt;So, the better approach is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep &lt;strong&gt;high cohesion&lt;/strong&gt; inside your modules&lt;/li&gt;
&lt;li&gt;Keep &lt;strong&gt;loose coupling&lt;/strong&gt; between modules&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sum up
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Cohesion - communication inside one module&lt;/li&gt;
&lt;li&gt;Coupling - communication between different modules&lt;/li&gt;
&lt;li&gt;Coupling and Cohesion are linked, one does not exists without the other.&lt;/li&gt;
&lt;li&gt;Keep coupling low by reducing the number of coupling points.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Information and inspiration source
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Clean Code&lt;/em&gt; by Robert C. Martin &lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>beginners</category>
      <category>cleancode</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
