<?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: cakoko</title>
    <description>The latest articles on DEV Community by cakoko (@cakoko).</description>
    <link>https://dev.to/cakoko</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%2F768371%2F333decc8-1509-4662-9fd6-d7416604e4d5.PNG</url>
      <title>DEV Community: cakoko</title>
      <link>https://dev.to/cakoko</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cakoko"/>
    <language>en</language>
    <item>
      <title>Open Closed Principle (OCP): Theory and Implementation in Swift</title>
      <dc:creator>cakoko</dc:creator>
      <pubDate>Fri, 03 Apr 2026 11:35:40 +0000</pubDate>
      <link>https://dev.to/cakoko/open-closed-principle-ocp-theory-and-implementation-in-swift-714</link>
      <guid>https://dev.to/cakoko/open-closed-principle-ocp-theory-and-implementation-in-swift-714</guid>
      <description>&lt;h1&gt;
  
  
  Introduction to Open Close Principle
&lt;/h1&gt;

&lt;p&gt;Open-Closed Principle (OCP) is one of the principles in SOLID that was popularized by Uncle Bob. The idea originally comes from Bertrand Meyer in &lt;em&gt;Object-Oriented Software Construction&lt;/em&gt; (1988). In that book, Meyer defines this principle as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What’s confusing about OCP
&lt;/h2&gt;

&lt;p&gt;The first thing I got from the title of this principle was&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;we should try to predict how the code may grow from the start, so later it can be extended without modifying the existing code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So in my head, &lt;strong&gt;open&lt;/strong&gt; sounded like “prepare the code so it can be extended later,” and &lt;strong&gt;closed&lt;/strong&gt; sounded like “once the code is written, it should not be modified anymore.”  From that interpretation, I ended up thinking that we should &lt;strong&gt;predicting future changes from the beginning&lt;/strong&gt;, creating extension points early (hole), and then extending the code through those “holes” instead of modifying the existing implementation.&lt;/p&gt;

&lt;p&gt;I wrote an &lt;a href="https://medium.com/swift-dev-club/open-closed-principle-o-in-solid-theory-fbd164d6288" rel="noopener noreferrer"&gt;article about OCP&lt;/a&gt; that was published in 2023. At that time, I also did some research while writing it, and that made me think this interpretation was correct. Many articles and tutorials out there also explain it in a very similar way, so the misunderstanding can feel valid at first. But the more I code, the more confusing that way of thinking became. It is really hard to predict those things when we are just starting to write code. And when I failed to predict it correctly, I got that bad feeling, like I was breaking the principle just because I needed to modify the code. On the other hand, trying to make the right prediction from the start also gave me anxiety for making it wrong.&lt;/p&gt;

&lt;p&gt;That confusion stayed with me for quite a while, until I started this series and tried to relearn this principle. And that is where I found an article with a comment that criticized the way the writer explained OCP. The writer basically explained it like how the old me used to understand it. The person in the comment then referenced another article written more than a decade ago, in 2013, by Jon Skeet. Here is the article: &lt;a href="https://codeblog.jonskeet.uk/2013/03/15/the-open-closed-principle-in-review" rel="noopener noreferrer"&gt;https://codeblog.jonskeet.uk/2013/03/15/the-open-closed-principle-in-review&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Jon Skeet’s view about OCP
&lt;/h2&gt;

&lt;p&gt;In his article, Jon Skeet explains that his issue is not really with the low-level design idea behind OCP. In fact, he says the lower-level explanation of what is good and bad in the design example is actually fine. His problem is more with the &lt;strong&gt;name&lt;/strong&gt; and the &lt;strong&gt;high-level wording&lt;/strong&gt; of the principle, because they do not explain the real idea clearly enough.&lt;/p&gt;

&lt;p&gt;What I think is important from his article is, the more useful way to understand OCP is not to focus too much on the words &lt;strong&gt;open&lt;/strong&gt; and &lt;strong&gt;closed&lt;/strong&gt;, but to focus on &lt;strong&gt;how change in one part should not force other parts to keep changing too&lt;/strong&gt;. Skeet highlights Craig Larman’s explanation that being “closed with respect to X” means that &lt;strong&gt;clients are not affected if X changes&lt;/strong&gt;. Here, &lt;em&gt;clients&lt;/em&gt; does not mean customers or end users. It means the other parts of the code that depend on that module, class, or interface. &lt;/p&gt;

&lt;p&gt;He also points out that OCP is easier to understand when we stop thinking about just one module alone. In practice, this is usually about a &lt;strong&gt;relationship between pieces of code&lt;/strong&gt;. There is one part that may change, and there is another part that depends on it. The goal is to design the boundary between them so that when the first part changes, the second part does not need to change every time as well.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;One side contains the variation, and the other side depends on a stable contract so it does not need to know every concrete case.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is also why Skeet finds another principle called &lt;strong&gt;Protected Variation&lt;/strong&gt; is much clearer. The phrase he quotes is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Identify points of predicted variation and create a stable interface around them.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I think this captures the idea much better, because it gives a more practical angle. First, try to see where variation is already visible, repeatedly requested, or naturally expected by the domain. Then, create a stable boundary around that part, so the rest of the code does not need to be affected by that change again and again.&lt;/p&gt;

&lt;p&gt;But Skeet also doesn't make it sound easy. He points out that predicting variation is hard. If we predict too much, we can end up over-engineering. If we predict too little, we may still need to change a lot of code later or add a new interface after the fact.&lt;/p&gt;

&lt;p&gt;So, for me, the most useful takeaway from his article is this: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;a better way to see OCP is not as &lt;strong&gt;“never modify code again”&lt;/strong&gt;, but as &lt;strong&gt;“find the parts that are likely to change, then design things so that those changes do not keep spreading to other parts of the system.”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Better Angle to See OCP / The Mental Model
&lt;/h2&gt;

&lt;p&gt;From that perspective, the way I look at OCP now is slightly different from how I understood it before. It is not really about &lt;strong&gt;never modifying code again&lt;/strong&gt;. In real software development, code will always change. Requirements change, new features appear, and old assumptions turn out to be wrong. Trying to completely avoid modification is unrealistic.&lt;/p&gt;

&lt;p&gt;A more useful way to see OCP is to focus on the areas where variation is visible, likely, or meaningful enough to justify a stable boundary, and then design the system so that those changes do not keep forcing other parts of the system to change as well.&lt;/p&gt;

&lt;p&gt;This does not mean we must predict every future change from the beginning. That would be hard and can easily lead to over-engineering. A more practical approach is to notice which part of the system is likely to keep changing, then place a stable boundary around that part so the rest of the code does not need to change every time too.&lt;/p&gt;

&lt;p&gt;In many systems, some behaviors are more likely to evolve than others. For example, business rules may change, new payment methods may be added, or new output formats may be required. These are places where variation naturally appears.&lt;/p&gt;

&lt;p&gt;Instead of letting those changes spread across many parts of the code, we try to &lt;strong&gt;isolate that variation behind a stable boundary&lt;/strong&gt;. The parts we want to keep stable can then depend on that interface instead of depending directly on the concrete implementations behind it.&lt;/p&gt;

&lt;p&gt;In many designs, one common way variation first appears is as repeatedly edited branching logic in one place. After refactoring, that variation can be moved behind a stable abstraction so new behavior is added without repeatedly editing that same part.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;OCP is most useful when variation is meaningful and recurring, not when we are only imagining many future possibilities without evidence.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The more stable parts of the design stay relatively unchanged, while new behavior is added behind the same boundary.&lt;/p&gt;

&lt;p&gt;So the mental model I use for OCP now is this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Identify where change is likely to happen, isolate that variation, and design a stable boundary so the rest of the system does not need to change because of it.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or in simpler questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which behavior in this system is likely to vary?&lt;/li&gt;
&lt;li&gt;Which parts of the system depend on that behavior?&lt;/li&gt;
&lt;li&gt;Can I introduce a stable interface so those parts do not need to change every time the behavior evolves?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the next section, we will look at a small example to see how this idea appears in actual code.&lt;/p&gt;




&lt;h1&gt;
  
  
  How OCP Looks in Code
&lt;/h1&gt;

&lt;p&gt;Imagine we are building a checkout feature. The checkout flow needs to request a payment for an order. At first, the system only supports one payment provider, for example Stripe. Later, the business wants to add Xendit. After that, maybe another provider is added.&lt;/p&gt;

&lt;p&gt;We can model the basic data like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;enum&lt;/span&gt; &lt;span class="kt"&gt;PaymentProvider&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;stripe&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;xendit&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;Order&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;totalAmount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Decimal&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;PaymentRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Decimal&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;enum&lt;/span&gt; &lt;span class="kt"&gt;PaymentStatus&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;success&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;failed&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;PaymentResult&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PaymentStatus&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A common early implementation is to place both the provider selection and the provider-specific behavior inside the same checkout service&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;CheckoutService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;requestPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PaymentProvider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;throws&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;PaymentResult&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;PaymentRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nv"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nv"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;totalAmount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nv"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;currency&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;// Build Stripe-specific payload&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;providerResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="cm"&gt;/* call Stripe API */&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;PaymentResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nv"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;providerResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;success&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;xendit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;// Build Xendit-specific payload&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;providerResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="cm"&gt;/* call Xendit API */&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;PaymentResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nv"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;providerResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;success&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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Writer Note:&lt;br&gt;
The problem is not the switch itself, but that CheckoutService becomes the place where provider variation accumulates&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For now, this may still look good or acceptable. The service receives the order, checks the selected provider, and performs the payment. The problem appears when the system grows. Every time a new provider is added, the same CheckoutService must be modified again.&lt;/p&gt;

&lt;p&gt;That means the checkout flow is doing two jobs at once:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;coordinating the payment request flow&lt;/li&gt;
&lt;li&gt;holding provider-specific integration logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the same class becomes the place where all provider variation keeps accumulating. This is the part that starts to violate the spirit of OCP.&lt;/p&gt;

&lt;p&gt;The issue is not that the business requirement is wrong. The system still truly needs to use the selected provider. The issue is that the variation and the stable flow are both living inside the same class.&lt;/p&gt;

&lt;p&gt;This is where OCP gives us a better direction. We still keep the same requirement, but we move the provider-specific behavior behind a stable abstraction.&lt;/p&gt;

&lt;p&gt;First, we define a contract/abstraction for payment gateways&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;protocol&lt;/span&gt; &lt;span class="kt"&gt;PaymentGateway&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PaymentProvider&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;requestPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PaymentRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;throws&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;PaymentResult&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then each provider can implement that contract in its own class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;StripePaymentGateway&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PaymentGateway&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PaymentProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stripe&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;requestPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PaymentRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;throws&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;PaymentResult&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Build Stripe-specific payload&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;providerResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="cm"&gt;/* call Stripe API */&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;PaymentResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nv"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;providerResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;success&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;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;XenditPaymentGateway&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PaymentGateway&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PaymentProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xendit&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;requestPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PaymentRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;throws&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;PaymentResult&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Build Xendit-specific payload&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;providerResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="cm"&gt;/* call Xendit API */&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;PaymentResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nv"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;providerResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;success&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 this point, we still have the same business requirement as before&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;given an order and a selected provider, the system must use the correct provider to request payment. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So the goal is not to remove provider selection completely. The goal is to stop putting the selection and all provider-specific implementation details into one growing checkout service.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Orchestration
&lt;/h2&gt;

&lt;p&gt;After separating the provider-specific logic behind focused classes, we still need a way to coordinate them into one complete checkout flow. This is where orchestration comes in.&lt;/p&gt;

&lt;p&gt;In this case, orchestration means one part of the code is responsible for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;receiving the order and selected provider&lt;/li&gt;
&lt;li&gt;preparing the payment request&lt;/li&gt;
&lt;li&gt;resolving the correct payment gateway&lt;/li&gt;
&lt;li&gt;delegating the payment work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It does not take over the provider-specific responsibilities. Instead, it coordinates the flow while each concrete gateway handles its own integration details.&lt;/p&gt;

&lt;p&gt;To support that flow, we first define a small abstraction for resolving the correct gateway.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;protocol&lt;/span&gt; &lt;span class="kt"&gt;PaymentGatewayResolving&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PaymentProvider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throws&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;PaymentGateway&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This protocol gives the checkout flow a stable way to ask for the correct payment gateway. The checkout flow does not need to know whether the gateway comes from a dictionary, a factory, or some other setup. It only needs to know that, given a provider, it can get the correct PaymentGateway. After that we create implementation of it using registry.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;enum&lt;/span&gt; &lt;span class="kt"&gt;PaymentGatewayResolverError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;gatewayNotFound&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;PaymentGatewayRegistry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PaymentGatewayResolving&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;gateways&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;PaymentProvider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PaymentGateway&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gateways&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;PaymentGateway&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gateways&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nv"&gt;uniqueKeysWithValues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;gateways&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$0&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;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PaymentProvider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throws&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;PaymentGateway&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;gateway&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gateways&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="kt"&gt;PaymentGatewayResolverError&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gatewayNotFound&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;gateway&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;The responsibility of choosing the correct gateway is still present, but it is no longer mixed with provider-specific payment behavior inside the same service. PaymentGatewayRegistry stores the configured gateways and returns the correct one based on the provider. Its responsibility is to manage gateway lookup over the registered implementations. It doesn't build payment requests, it doesn't call external APIs, and it doesn't coordinate checkout. That is why this class stays focused.&lt;/p&gt;

&lt;p&gt;The resolver needs to know which gateway implementations are available. A simplified setup can register them like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;gateways&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;PaymentGateway&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="kt"&gt;StripePaymentGateway&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="kt"&gt;XenditPaymentGateway&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;gatewayRegistry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;PaymentGatewayRegistry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gateways&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;gateways&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setup shows that the available gateway implementations are registered once, then made available through the registry. If a new provider is added later, we add a new PaymentGateway implementation and register it here. In this simplified example, the PaymentProvider enum also still needs to be updated. The important point is that the checkout flow itself no longer needs to accumulate provider-specific branches.&lt;/p&gt;

&lt;p&gt;So in this example, OCP does not eliminate modification from the whole system. It mainly prevents CheckoutService from being the place that keeps changing.&lt;/p&gt;

&lt;p&gt;With that in place, CheckoutService can focus on the business flow itself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;CheckoutService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;gatewayResolver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PaymentGatewayResolving&lt;/span&gt;

    &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gatewayResolver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PaymentGatewayResolving&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gatewayResolver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gatewayResolver&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;requestPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PaymentProvider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;throws&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;PaymentResult&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;PaymentRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nv"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nv"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;totalAmount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nv"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;currency&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;gateway&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="n"&gt;gatewayResolver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;for&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;provider&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;try&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;gateway&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requestPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&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 checkout flow no longer depends directly on concrete provider-specific integration logic. Instead, it depends on abstractions that separate the stable checkout flow from provider-specific behavior.&lt;/p&gt;

&lt;p&gt;This is still the same use case as the original version.&lt;/p&gt;

&lt;p&gt;The checkout flow still:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;receives the order&lt;/li&gt;
&lt;li&gt;receives the selected provider&lt;/li&gt;
&lt;li&gt;builds the payment request&lt;/li&gt;
&lt;li&gt;uses the correct provider&lt;/li&gt;
&lt;li&gt;returns a payment result&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The difference is in where the responsibilities now live.&lt;/p&gt;

&lt;h4&gt;
  
  
  Before
&lt;/h4&gt;

&lt;p&gt;CheckoutService:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;selected the provider&lt;/li&gt;
&lt;li&gt;contained Stripe logic&lt;/li&gt;
&lt;li&gt;contained Xendit logic&lt;/li&gt;
&lt;li&gt;kept growing as variation increased&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  After
&lt;/h4&gt;

&lt;p&gt;CheckoutService:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;coordinates the checkout payment flow&lt;/li&gt;
&lt;li&gt;asks another component to resolve the correct gateway&lt;/li&gt;
&lt;li&gt;delegates provider-specific work to the gateway&lt;/li&gt;
&lt;li&gt;remains relatively stable as new providers are added&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;StripePaymentGateway and XenditPaymentGateway:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;own their own integration logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;PaymentGatewayRegistry:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;knows how to find the correct gateway for a provider&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the key improvement.&lt;/p&gt;

&lt;p&gt;In the original version, the switch both selected the provider and contained the provider-specific implementation logic. In the refactored version, the checkout flow still works with the same provider selection requirement, but the implementation logic has been moved behind stable abstractions.&lt;/p&gt;

&lt;p&gt;In this example, the OCP improvement is this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;instead of handling variation by repeatedly modifying the same branching logic, we handle it by adding new implementations behind a stable abstraction.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In other cases, OCP may look different. The variation might be moved into configuration, separate rule objects, plugins, handlers, factories, or another module boundary. So the point is not that OCP always means replacing switch with protocols. The point is to separate the part that changes, so the stable part does not need to be modified again and again.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is being protected here?
&lt;/h2&gt;

&lt;p&gt;In this example, the variation is the payment provider integration. The more stable part is the checkout flow that needs to request a payment for an order.&lt;/p&gt;

&lt;p&gt;By introducing PaymentGateway, the checkout flow no longer needs to know how each provider behaves internally. By introducing PaymentGatewayResolving, CheckoutService no longer needs to know how the provider lookup is implemented.&lt;/p&gt;

&lt;p&gt;So the business flow stays the same, but the provider-specific variation no longer keeps spreading into the same service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is this “true OCP”?
&lt;/h2&gt;

&lt;p&gt;In my POV, this is a good practical OCP improvement.&lt;/p&gt;

&lt;p&gt;It does &lt;strong&gt;not&lt;/strong&gt; mean nothing in the whole system ever changes again. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a new provider may still require a new PaymentGateway implementation&lt;/li&gt;
&lt;li&gt;the PaymentProvider enum may still need to be updated&lt;/li&gt;
&lt;li&gt;the list of registered gateways may still need to be updated
That is okay.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The point of OCP here is that the &lt;strong&gt;stable checkout flow&lt;/strong&gt; is no longer the place that must keep accumulating provider-specific branches and logic every time the variation grows. The modifications are reduced and pushed toward the variation side of the design instead.&lt;/p&gt;

&lt;p&gt;This is only a simplified example to show the OCP boundary clearly. In a real system, the resolver may be backed by a registry, dependency injection setup, or some other composition mechanism. Those details are omitted here so the example can stay focused on the main design change, the provider variation has been moved behind a stable boundary.&lt;/p&gt;

&lt;p&gt;Final note:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;OCP here does not remove modification completely. It moves most of the modification pressure away from the stable checkout flow and toward the variation side of the design.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  From the Writer
&lt;/h1&gt;

&lt;p&gt;Hello, allow me to introduce myself. I’m Cakoko. We’ve reached the end of this article, and I sincerely thank you for taking the time to read it.&lt;/p&gt;

&lt;p&gt;If you have any questions or feedback, feel free to reach out to me directly via email at &lt;a href="mailto:cakoko.dev@gmail.com"&gt;cakoko.dev@gmail.com&lt;/a&gt;. I’m more than happy to hear your thoughts, whether they are about my English writing, the technical ideas in this article, or anything I may have misunderstood. Your feedback will help me grow.&lt;/p&gt;

&lt;p&gt;I look forward to connecting with you in future articles. Btw, I’m a mobile developer, final year Computer Science student, and an Apple Developer Academy @ IL graduate. I’m also open to various opportunities such as collaborations, internships, or full-time positions. It would make me very happy to explore those possibilities.&lt;/p&gt;

&lt;p&gt;Until next time, stay curious and keep learning.&lt;/p&gt;




&lt;h1&gt;
  
  
  Open for Feedback
&lt;/h1&gt;

&lt;p&gt;This article is part of my personal learning journey. It may not be completely accurate or perfect, and that is okay. I’m sharing what I’ve learned so far in the hope that it can also help others who are exploring similar topics.&lt;/p&gt;

&lt;p&gt;If you have any feedback, suggestions, or corrections, I would truly appreciate them. I’m always open to learning more and improving along the way.&lt;/p&gt;

</description>
      <category>softwaredevelopment</category>
      <category>architecture</category>
      <category>swift</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Single Responsibility Principle (SRP): Theory and Implementation in Swift</title>
      <dc:creator>cakoko</dc:creator>
      <pubDate>Sun, 29 Mar 2026 03:56:02 +0000</pubDate>
      <link>https://dev.to/cakoko/single-responsibility-principle-srp-theory-and-implementation-in-swift-3f83</link>
      <guid>https://dev.to/cakoko/single-responsibility-principle-srp-theory-and-implementation-in-swift-3f83</guid>
      <description>&lt;h1&gt;
  
  
  What is Single Responsibility Principle
&lt;/h1&gt;

&lt;p&gt;SRP, or Single Responsibility Principle, states that a class should have only one reason to change. In other words, the class should represent one cohesive responsibility. If a class changes for multiple unrelated reasons, it is likely handling more than one responsibility.&lt;/p&gt;

&lt;p&gt;When unrelated responsibilities are mixed in one class, the class becomes less cohesive. A change for one concern can affect code that serves another concern, which makes the class harder to test, maintain, and modify safely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mental Model
&lt;/h2&gt;

&lt;p&gt;The mental model of SRP is to think about &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;how many reasons a class has to change&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Another way to think about it is to ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How many kinds of changes could be requested for this class?&lt;/li&gt;
&lt;li&gt;How many sources of change affect this class?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In &lt;a href="https://en.wikipedia.org/wiki/Robert_C._Martin" rel="noopener noreferrer"&gt;Uncle Bob&lt;/a&gt;’s explanation here &lt;a href="https://www.youtube.com/watch?v=zHiWqnTWsn4&amp;amp;t=3609s" rel="noopener noreferrer"&gt;Uncle Bob SOLID principles&lt;/a&gt;, these sources of change are often described in terms of different &lt;strong&gt;actors&lt;/strong&gt;. An actor represents a person or group that interacts with the system and may request changes, such as business stakeholders, UI designers, database engineers, or other teams. This is a useful lens, because different actors often introduce different kinds of change. However, the deeper idea is still the same: a class should not be pulled by multiple unrelated reasons to change.&lt;/p&gt;

&lt;p&gt;If a class needs to respond to requests from multiple unrelated actors, that can be a sign that the class is handling multiple responsibilities. Each actor may request changes for different reasons, which can cause the class to change in unrelated ways.&lt;/p&gt;

&lt;p&gt;Because of that, a class should ideally serve one &lt;strong&gt;cohesive source of change&lt;/strong&gt;. In many cases, that also means it mainly serves one actor, or one closely related group of actors.&lt;/p&gt;

&lt;p&gt;A class is cohesive when its methods naturally belong together and tend to change together for the same reason.&lt;/p&gt;

&lt;h2&gt;
  
  
  Difference between SoC (Separation of Concern)
&lt;/h2&gt;

&lt;p&gt;Since some of you may have come to this article from my series &lt;a href="https://dev.to/cakoko/series/37430"&gt;What I’m Learning About Writing Better Structured Code: A Learning Series&lt;/a&gt; may have already read about &lt;a href="https://dev.to/cakoko/seeing-the-problem-an-introduction-to-separation-of-concerns-4l1"&gt;Seeing the problem: An Introduction to Separation of Concerns&lt;/a&gt; , you might be wondering what the difference is between SRP and SoC. This section is only a quick explanation of the difference. I will write another article about it later.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The difference is that &lt;strong&gt;SoC is a broader idea&lt;/strong&gt;, while &lt;strong&gt;SRP is a more specific design principle&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Separation of Concerns is about separating different kinds of work in a system, so each part focuses on its own concern. For example, UI rendering, business logic, networking, and data storage are different concerns, and we usually try to keep them separated.&lt;/p&gt;

&lt;p&gt;The Single Responsibility Principle is more specific. It helps us judge whether a particular unit, such as a class, forms one cohesive responsibility and has one reason to change, while SoC is a broader way of thinking about separating different kinds of work across a system.&lt;/p&gt;

&lt;p&gt;So they are related, but they are not exactly the same. SoC helps us think about separating different concerns in general, while SRP helps us judge whether a specific class or unit is trying to handle too much.&lt;/p&gt;

&lt;h2&gt;
  
  
  OOP missconception.
&lt;/h2&gt;

&lt;p&gt;lets look at this class as an example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;PayslipProcessor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;calculateNetSalary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Employee&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;generatePayslipContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Employee&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;sendPayslip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nv"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Employee&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&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 class may look fine right? Its a PayslipProcessor object, and all of its functions seem related to processing payslips.&lt;/p&gt;

&lt;p&gt;I used to think like that too. Maybe one of the reasons is because that is how OOP (Object Oriented Programming) is often introduced in college and in many tutorials. A class is usually explained as a &lt;strong&gt;representation of a real-world object&lt;/strong&gt;. Because of that, it's easy to think, “If this is a payslip processor object, then it makes sense if it contains all functions related to payslip processing”.&lt;/p&gt;

&lt;p&gt;And if we continue with that way of thinking, it can still sound reasonable. In real life, someone in payroll might calculate salary, prepare the payslip content, and send it to the employee. So from that pov, putting all of those functions into one class can feel correct. But this is where the problem starts. The mistake is that we group things based on the fact that they look related on the surface. Yes, they are all related to payslip processing. But that does not automatically mean they are the same responsibility.&lt;/p&gt;

&lt;p&gt;These are separate responsibilities not simply because they are different actions, but because they are affected by different kinds of changes. Salary calculation changes when payroll rules change. Payslip content changes when content or compliance requirements change. Payslip delivery changes when the delivery mechanism or integration changes.&lt;/p&gt;

&lt;p&gt;This is why thinking about a class only as a real-world object is sometimes not enough. It can push us to put too many responsibilities into one class just because they look like they belong together. That is why SRP gives us a better way to judge it.&lt;/p&gt;

&lt;p&gt;Instead of only asking, &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Does this function belong to this object?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;we should also ask&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“How many reasons does this class have to change?”&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Implementing SRP
&lt;/h1&gt;

&lt;p&gt;So how do we actually implement SRP?&lt;/p&gt;

&lt;p&gt;The main idea is not to split a class just because it has many methods, or because the file looks long. That can happen, but that is not the real point.&lt;/p&gt;

&lt;p&gt;The real point is to look at a class and ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How many kinds of changes could be requested for this class?&lt;/li&gt;
&lt;li&gt;How many sources of change affect this class?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s go back to the previous example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;PayslipProcessor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;calculateNetSalary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Employee&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;generatePayslipContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Employee&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;sendPayslip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nv"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Employee&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&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, this class looks good because everything is still about payslips. But if we inspect it using the SRP mental model, we can see that the class is being pulled by different kinds of changes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The salary calculation can change when payroll rules change. Maybe finance changes the overtime rule, tax rule, or deduction formula.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The payslip content can change when HR or compliance requests a different format, adds a required field, or changes the wording.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The delivery requirements can change. For example, the payslip may need to be emailed, uploaded to another system, or sent through a different channel.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These changes do not come from the same reason. They may all happen in the same feature area, but they are still different responsibilities. That is the important part. SRP is not telling us to split code randomly. It is telling us to separate parts that change for different reasons.&lt;/p&gt;

&lt;p&gt;So instead of keeping all of those things inside one class, we can separate them by responsibility:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;SalaryCalculator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;calculateNetSalary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Employee&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;PayslipContentGenerator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;generatePayslipContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Employee&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;PayslipSender&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;sendPayslip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nv"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Employee&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&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 is one possible way to create clearer responsibility boundaries. In a real system, the exact split may vary depending on how the domain and workflow are designed, but the main idea stays the same: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;separate parts that change for different reasons.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The exact responsibility boundary is often contextual. What matters is not following a fixed pattern, but identifying which parts of the code change together and which change independently.&lt;/p&gt;

&lt;p&gt;Now each class has a more focused reason to change.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If payroll rules change, we know the first class is the one that should be affected.&lt;/li&gt;
&lt;li&gt;If the payslip content changes, we know the second class is the one that should be affected.&lt;/li&gt;
&lt;li&gt;If the delivery mechanism changes, we know the third class is the one that should be affected.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is a much better boundary. And that is the core of implementing SRP:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;we separate code based on &lt;strong&gt;different sources of change&lt;/strong&gt;, not just based on whether functions look related at a glance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is also why SRP is not just about making classes small. A class can still have multiple methods and still follow SRP, as long as those methods support the same responsibility and are affected by the same kind of change. Different methods or steps do not automatically mean different responsibilities. If they always change together for the same reason, they may still belong in the same class.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Orchestration
&lt;/h2&gt;

&lt;p&gt;After separating responsibilities into focused classes, we still need a way to coordinate them into one complete workflow. This is where orchestration comes in.&lt;/p&gt;

&lt;p&gt;Orchestration means one part of the code is responsible for managing the flow between these smaller classes. It does not take over their responsibilities. Instead, it calls them in the right order to complete one use case.&lt;/p&gt;

&lt;p&gt;A simplified example might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;PayslipService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;salaryCalculator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;SalaryCalculator&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;contentGenerator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PayslipContentGenerator&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PayslipSender&lt;/span&gt;

    &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nv"&gt;salaryCalculator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;SalaryCalculator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;contentGenerator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PayslipContentGenerator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PayslipSender&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;salaryCalculator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;salaryCalculator&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contentGenerator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;contentGenerator&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;processPayslip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Employee&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;salary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;salaryCalculator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;calculateNetSalary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;for&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;contentGenerator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generatePayslipContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;for&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendPayslip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;employee&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;The important point is that PayslipService coordinates the workflow, but the responsibility of calculation, content generation, and delivery still stays in their own classes.&lt;/p&gt;

&lt;p&gt;This is only a simplified example to show the role of orchestration. In a real system, the design can be improved further depending on the architecture and the complexity of the workflow. I will cover that in another article in this &lt;a href="https://dev.to/cakoko/series/37681"&gt;SOLID series&lt;/a&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  From the Writer
&lt;/h1&gt;

&lt;p&gt;Hello, allow me to introduce myself. I’m Cakoko. We’ve reached the end of this article, and I sincerely thank you for taking the time to read it.&lt;/p&gt;

&lt;p&gt;If you have any questions or feedback, feel free to reach out to me directly via email at &lt;a href="mailto:cakoko.dev@gmail.com"&gt;cakoko.dev@gmail.com&lt;/a&gt;. I’m more than happy to hear your thoughts, whether they are about my English writing, the technical ideas in this article, or anything I may have misunderstood. Your feedback will help me grow.&lt;/p&gt;

&lt;p&gt;I look forward to connecting with you in future articles. Btw, I’m a mobile developer, final year Computer Science student, and an Apple Developer Academy @ IL graduate. I’m also open to various opportunities such as collaborations, internships, or full-time positions. It would make me very happy to explore those possibilities.&lt;/p&gt;

&lt;p&gt;Until next time, stay curious and keep learning.&lt;/p&gt;




&lt;h1&gt;
  
  
  Open for Feedback
&lt;/h1&gt;

&lt;p&gt;This article is part of my personal learning journey. It may not be completely accurate or perfect, and that is okay. I’m sharing what I’ve learned so far in the hope that it can also help others who are exploring similar topics.&lt;/p&gt;

&lt;p&gt;If you have any feedback, suggestions, or corrections, I would truly appreciate them. I’m always open to learning more and improving along the way.&lt;/p&gt;

</description>
      <category>softwaredevelopment</category>
      <category>architecture</category>
      <category>swift</category>
      <category>mobile</category>
    </item>
    <item>
      <title>SOLID Principle: Theory and Implementation in Swift</title>
      <dc:creator>cakoko</dc:creator>
      <pubDate>Sun, 29 Mar 2026 03:53:26 +0000</pubDate>
      <link>https://dev.to/cakoko/solid-principle-theory-and-implementation-in-swift-4pmi</link>
      <guid>https://dev.to/cakoko/solid-principle-theory-and-implementation-in-swift-4pmi</guid>
      <description>&lt;p&gt;This is a series about the &lt;strong&gt;SOLID principles&lt;/strong&gt; introduced by Uncle Bob (Robert C. Martin). SOLID is a set of design principles that help us write code that is easier to understand, maintain, and evolve over time.&lt;/p&gt;

&lt;p&gt;SOLID consists of five principles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;S - Single Responsibility Principle&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;read here : &lt;a href="https://dev.to/cakoko/single-responsibility-principle-srp-theory-and-implementation-in-swift-3f83"&gt;https://dev.to/cakoko/single-responsibility-principle-srp-theory-and-implementation-in-swift-3f83&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;O - Open Closed Principle&lt;/strong&gt;&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;L - Liskov Substitution Principle&lt;/strong&gt;&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;I - Interface Segregation Principle&lt;/strong&gt;&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;D - Dependency Inversion Principle&lt;/strong&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Each of these principles tries to solve a different kind of design problem, but they all share the same goal: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;helping us write code that stays manageable as the system grows.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Why I’m Writing This Series
&lt;/h3&gt;

&lt;p&gt;This article series is created while I’m working on my other series:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/cakoko/what-im-learning-about-writing-better-structured-code-a-learning-series-k5l"&gt;What I’m Learning About Writing Better Structured Code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While writing that series, I realized something. Many of the ideas I want to explain are closely related to SOLID. But explaining them properly requires a deeper discussion about these principles themselves.&lt;/p&gt;

&lt;p&gt;So instead of trying to squeeze everything into that series, I decided to write a more focused set of articles about SOLID. Later, I can reference these articles whenever the topic appears again.&lt;/p&gt;




&lt;h3&gt;
  
  
  A Small Personal Story
&lt;/h3&gt;

&lt;p&gt;This series is also a bit personal for me.&lt;/p&gt;

&lt;p&gt;Around three years ago, I wrote two article series on Medium about SOLID:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://mrezkys.medium.com/list/solid-principle-explained-by-me-c2e5137f1576" rel="noopener noreferrer"&gt;SOLID Principle explained by me&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://mrezkys.medium.com/list/solid-principle-practical-with-me-b9d37878ad6c" rel="noopener noreferrer"&gt;SOLID Principle practical with me&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Looking back at them now, I can see that some parts were weak, some parts were missing important context, and some parts were simply wrong.&lt;/p&gt;

&lt;p&gt;But honestly, that realization is actually the fun part. It means that over the past few years I’ve gone through a lot of &lt;strong&gt;learn, unlearn, and relearn&lt;/strong&gt; moments. When I read those old articles now, I can clearly see how surface-level my understanding was back then.&lt;/p&gt;

&lt;p&gt;And that also means I’ve improved.&lt;/p&gt;

&lt;p&gt;This series is my attempt to revisit SOLID again, with a clearer understanding and better explanations.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>softwaredevelopment</category>
      <category>swift</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Judging the Problem: Code Smells and Refactoring</title>
      <dc:creator>cakoko</dc:creator>
      <pubDate>Wed, 25 Mar 2026 11:46:01 +0000</pubDate>
      <link>https://dev.to/cakoko/judging-the-problem-code-smells-and-refactoring-5aee</link>
      <guid>https://dev.to/cakoko/judging-the-problem-code-smells-and-refactoring-5aee</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;After learning about Separation of Concerns, how to see a problem, and how to start separating it in the &lt;a href="https://dev.to/cakoko/seeing-the-problem-an-introduction-to-separation-of-concerns-4l1"&gt;first article&lt;/a&gt;, now we move to the next step, learning how to judge the problem.&lt;/p&gt;

&lt;p&gt;Once we can identify structural problems in code, the next question is not just what the problems are, but which ones need more attention. Which problems should be solved as soon as possible, and which ones are still good enough for the current context?&lt;/p&gt;

&lt;p&gt;The main difference from the previous article is that the question is no longer just:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;what concerns exist in this code?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;but now becomes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;where is the code structure starting to become unhealthy?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This part is about learning how to look at working code and ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what kind of structural issue is happening here?&lt;/li&gt;
&lt;li&gt;how serious is it?&lt;/li&gt;
&lt;li&gt;what should be refactored first?&lt;/li&gt;
&lt;li&gt;what is a real problem, and what is only a smaller imperfection?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will learn about code smells that can be identified in code. But you need to remember its not about memorizing famous smell names. Its about learning how to &lt;strong&gt;judge structural risk&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  What is Code Smell
&lt;/h1&gt;

&lt;p&gt;Based on &lt;a href="https://www.geeksforgeeks.org/blogs/code-smell-a-general-introduction-and-its-type/" rel="noopener noreferrer"&gt;GeeksforGeeks article about Code Smells&lt;/a&gt;, The term code smell was first introduced by *&lt;strong&gt;&lt;em&gt;Kent Beck&lt;/em&gt;&lt;/strong&gt;* , an American Software Engineer and the creator of extreme programming. When we work on an application and write codes for it, we see a few patterns that are needed to be refactored. Those patterns either duplicates, or complicates, or might make code dependent on other code. Such patterns are called Code Smells and detection of such code is called Code Smelling.&lt;/p&gt;

&lt;p&gt;So in simple terms, a code smell is a sign that &lt;strong&gt;something in the code structure may cause problems later, even if the code still works right now&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So smells are more like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;warnings&lt;/li&gt;
&lt;li&gt;indicators&lt;/li&gt;
&lt;li&gt;structural pressure points&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This will help a question that came from first article&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If this code grows, where will changes become more expensive than they should be?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are many categories of code smells. Some of the main categories are Bloaters, Object-Orientation Abusers, Change Preventers, Dispensables, and Couplers. Each of these categories has its own examples. For instance, Bloaters include Long Method, Large Class, Primitive Obsession, Long Parameter List, and Data Clumps. You can read the more detailed explanation of each category on the &lt;a href="https://refactoring.guru/refactoring/smells" rel="noopener noreferrer"&gt;Refactoring Guru website&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To make them easier to remember, I asked ChatGPT to create a simplified version for me. After reviewing it, I think it is close enough to summarize the actual list above. So for this article, here are the code smell categories that I will use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;poor boundaries&lt;/li&gt;
&lt;li&gt;fragile change points&lt;/li&gt;
&lt;li&gt;duplication&lt;/li&gt;
&lt;li&gt;shotgun surgery&lt;/li&gt;
&lt;li&gt;large function / mixed responsibility&lt;/li&gt;
&lt;li&gt;god object&lt;/li&gt;
&lt;li&gt;feature envy&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Code Smell Category
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Poor boundaries
&lt;/h3&gt;

&lt;p&gt;Poor boundaries happen when different responsibilities are placed too close together, or when it is not clear where one responsibility should stop and another should begin. This usually makes the code harder to understand because input/output, state changes, business logic, and flow control start mixing in the same place. In simple terms, poor boundaries mean the code has weak separation between jobs that should be clearer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fragile change points
&lt;/h3&gt;

&lt;p&gt;Fragile change points are parts of the code where even a small change feels risky. A simple update can accidentally affect other behavior because the code is too tightly connected, too unclear, or too overloaded with responsibility. This smell matters because it shows where the code is starting to resist change, even if it still works today.&lt;/p&gt;

&lt;h3&gt;
  
  
  Duplication
&lt;/h3&gt;

&lt;p&gt;Duplication happens when the same logic, structure, or decision appears in more than one place. This is not only about identical lines of code. It can also mean repeating the same idea in slightly different forms. Duplication becomes a problem because when the rule changes, we may need to remember to update it in multiple places, and it becomes easy for those places to drift apart.&lt;/p&gt;

&lt;h3&gt;
  
  
  Shotgun surgery
&lt;/h3&gt;

&lt;p&gt;Shotgun surgery happens when one small change forces us to edit many different parts of the codebase. Instead of changing one clear owner, we have to “shoot” changes across multiple files, functions, or objects. This is a strong sign that responsibility ownership is weak, because one concern is spread too widely.&lt;/p&gt;

&lt;h3&gt;
  
  
  Large function / mixed responsibility
&lt;/h3&gt;

&lt;p&gt;A large function is not bad just because it is long. It becomes a smell when that function starts doing many unrelated jobs at once. This usually means user input, validation, business logic, state mutation, output rendering, or flow control are all being handled together. The real problem is not the size alone, but the mixed responsibility inside it.&lt;/p&gt;

&lt;h3&gt;
  
  
  God object
&lt;/h3&gt;

&lt;p&gt;A god object is an object that knows too much, owns too much, or controls too much. Instead of having one focused responsibility, it becomes the place where many unrelated concerns gather. This kind of object often becomes a central dependency in the system, which makes the code harder to understand, harder to test, and harder to change safely.&lt;/p&gt;

&lt;h3&gt;
  
  
  Feature envy
&lt;/h3&gt;

&lt;p&gt;Feature envy happens when a function seems more interested in another object’s data than its own. Instead of using its own responsibility well, it keeps reaching into another object and doing work that probably belongs there. This is usually a sign that behavior is living in the wrong place, and that the ownership of logic may need to be reconsidered.&lt;/p&gt;




&lt;h1&gt;
  
  
  Code Smell Severity
&lt;/h1&gt;

&lt;p&gt;Smell severity is about how serious a code smell is in a specific codebase and context.&lt;/p&gt;

&lt;p&gt;There are many ways to rate smell severity. A lot of research has explored this, including work that uses machine learning to detect and evaluate code smells. Different approaches may use different criteria and scales. But for this article, I will use a simpler rating system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;weak&lt;/li&gt;
&lt;li&gt;medium&lt;/li&gt;
&lt;li&gt;strong&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Smell severity is basically about this question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How serious is this issue in this specific codebase?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The important point is that a code smell category and its severity are not the same thing. A smell may exist, but its severity can still be weak, medium, or strong depending on the context. This means:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Not every true smell is automatically a strong smell.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Writer note:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Here is some research about it: &lt;br&gt;
&lt;a href="https://www.sciencedirect.com/science/article/abs/pii/S0950705117301880" rel="noopener noreferrer"&gt;https://www.sciencedirect.com/science/article/abs/pii/S0950705117301880&lt;/a&gt;, &lt;a href="https://www.sciencedirect.com/science/article/abs/pii/S0957417424003488" rel="noopener noreferrer"&gt;https://www.sciencedirect.com/science/article/abs/pii/S0957417424003488&lt;/a&gt;, &lt;a href="https://sol.sbc.org.br/index.php/sbes/article/view/30345" rel="noopener noreferrer"&gt;https://sol.sbc.org.br/index.php/sbes/article/view/30345&lt;/a&gt;&lt;br&gt;
You can find more using this keyword: code smell severity, code smell prioritization, code smell risk assessment, code smell severity classification, machine learning for code smell severity&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Understanding Smell Severity More
&lt;/h2&gt;

&lt;p&gt;When I first tried to understand this concept, I was confused about how to actually judge the code and give a rating. If I found one issue here and another issue there, should I rate both as strong just because both are real smells? That part confused me a lot.&lt;/p&gt;

&lt;p&gt;After doing more research and discussing it with GPT, I understood the idea better when I tested it with a simple comparison.&lt;/p&gt;

&lt;p&gt;For example, using Bahasa Indonesia for variable names in a codebase can be a real issue. It can reduce readability, especially if the team expects one shared language. But now compare that with a public function that can mutate the database in a dangerous way. Both are still valid problems, yet they clearly do not have the same severity.&lt;/p&gt;

&lt;p&gt;That comparison helped me see the difference more clearly. The first issue may still count as a smell, but in that context it feels weak or mild. The second one is much more serious because the risk and impact are much higher.&lt;/p&gt;

&lt;p&gt;This made me realize an important point:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;smell presence and smell severity are different judgments&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In other words, recognizing that a smell exists is only the first step. After that, I still need to judge how serious it is in the current codebase. That usually means comparing things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;risk&lt;/li&gt;
&lt;li&gt;impact&lt;/li&gt;
&lt;li&gt;centrality&lt;/li&gt;
&lt;li&gt;future damage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And those depend on the context, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the size of the app&lt;/li&gt;
&lt;li&gt;how often the smell appears&lt;/li&gt;
&lt;li&gt;how central that code is&lt;/li&gt;
&lt;li&gt;how much future pain it may create&lt;/li&gt;
&lt;li&gt;whether it is the main structural problem or just a side issue&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also means comparing one smell against the other smells in the same codebase, not judging it in isolation.&lt;/p&gt;

&lt;p&gt;That was the point where the whole idea of smell severity started to make much more sense to me.&lt;/p&gt;




&lt;h1&gt;
  
  
  Smell Classification for ToDo App
&lt;/h1&gt;

&lt;p&gt;We will still use this base code from previous article. Try to classify the smell category and its severity for this code by yourself before see my answer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Foundation&lt;/span&gt;

&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;TodoItem&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;isDone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;TodoItem&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;showMenu&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"=== Todo App ==="&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"1. Show all todos"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2. Add todo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"3. Toggle todo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"4. Remove todo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"5. Exit"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Choose:"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;showTodos&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="n"&gt;todos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isEmpty&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"No todos yet."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enumerated&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;mark&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isDone&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"[x]"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"[ ]"&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;. &lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;mark&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;"&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;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Enter todo title:"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;readLine&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trimmingCharacters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;in&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;whitespaces&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isEmpty&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;TodoItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;isDone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Todo added."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invalid title."&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;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;toggleTodo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;showTodos&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isEmpty&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Enter todo number to toggle:"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;readLine&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;index&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="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isDone&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Todo updated."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invalid number."&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;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Please enter a valid number."&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;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;removeTodo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;showTodos&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isEmpty&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Enter todo number to remove:"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;readLine&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;index&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="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;removed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Removed: &lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;removed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&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;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invalid number."&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;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Please enter a valid number."&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;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;runApp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;isRunning&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;isRunning&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;showMenu&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;choice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;readLine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;

        &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;showTodos&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"3"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;toggleTodo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"4"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;removeTodo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"5"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;isRunning&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Goodbye."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unknown option."&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;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;runApp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Smell Classification Answer
&lt;/h2&gt;

&lt;p&gt;Before giving the classification, I want to repeat one important thing, this is not about memorizing famous smell names and forcing every codebase into them. The goal is to judge structural risk in context. A smell may be real, but its severity still depends on how central it is, how much damage it can create, and how strongly it affects change in the current codebase.&lt;/p&gt;

&lt;p&gt;From my perspective, this is the smell classification I ended up with for the Todo app above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Poor boundaries : strong
&lt;/h3&gt;

&lt;p&gt;I identified &lt;strong&gt;poor boundaries&lt;/strong&gt; as a strong smell.&lt;/p&gt;

&lt;p&gt;My reasoning connects directly to the first article. In Module 1, I used boundaries such as input/output, state, data, app flow, and business logic as the main lens for reading the code. In this Todo app, functions like &lt;code&gt;addTodo()&lt;/code&gt;, &lt;code&gt;toggleTodo()&lt;/code&gt;, and &lt;code&gt;removeTodo()&lt;/code&gt; mix several of those together in the same place.&lt;/p&gt;

&lt;p&gt;For example, &lt;code&gt;addTodo()&lt;/code&gt; mixes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;input&lt;/li&gt;
&lt;li&gt;output&lt;/li&gt;
&lt;li&gt;state mutation&lt;/li&gt;
&lt;li&gt;todo-related logic/business logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That means the function does not have clear responsibility boundaries.&lt;/p&gt;

&lt;p&gt;The important refinement here is that this smell is not strong just because “there are many things inside one function”. It is strong because those responsibilities can change for different reasons. The UI text may change. The input mechanism may change. The todo rules may change. Those should not all force edits in the same function. That is why poor boundaries is a real &lt;strong&gt;strong&lt;/strong&gt; smell here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fragile change points : strong
&lt;/h3&gt;

&lt;p&gt;I also identified &lt;strong&gt;fragile change points&lt;/strong&gt; as strong.&lt;/p&gt;

&lt;p&gt;One example that helped me see this was imagining a model change. If I add something like priority to TodoItem, that could force changes to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;object creation&lt;/li&gt;
&lt;li&gt;input flow&lt;/li&gt;
&lt;li&gt;output display&lt;/li&gt;
&lt;li&gt;maybe sorting or filtering later&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also connected this to changing the medium of the app itself, for example moving from a CLI app to a graphical UI. Because input and output are mixed directly into the logic, changing the interface would be more painful than it should be.&lt;/p&gt;

&lt;p&gt;The important refinement here is that the model changing is not bad by itself. The pain happens because that change &lt;strong&gt;ripples into multiple mixed places&lt;/strong&gt;. That is why fragile change points are strong in this codebase: one concept change can leak through many parts of the current structure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Duplication : medium
&lt;/h3&gt;

&lt;p&gt;I noticed duplication in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;repeated empty checking&lt;/li&gt;
&lt;li&gt;repeated input validation&lt;/li&gt;
&lt;li&gt;the repeated overall shape between &lt;code&gt;toggleTodo()&lt;/code&gt; and r&lt;code&gt;emoveTodo()&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At first, I was unsure whether duplication might actually be strong, because repeated validation across multiple places can create real pain if I forget to update one of them. But after re thinking the severity, I think that the duplication here is &lt;strong&gt;medium&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Why not strong? Because the larger structural problems in this codebase are still:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;poor boundaries&lt;/li&gt;
&lt;li&gt;mixed responsibilities&lt;/li&gt;
&lt;li&gt;weak ownership&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So duplication definitely exists, but it is not the most central problem yet.&lt;/p&gt;

&lt;p&gt;One important lesson I got from this is that duplication is not only about copy-paste text. It can also appear as duplicated &lt;strong&gt;control-flow shape&lt;/strong&gt;. That helped me see the smell more broadly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Shotgun surgery : medium
&lt;/h3&gt;

&lt;p&gt;I understood &lt;strong&gt;shotgun surgery&lt;/strong&gt; as one conceptual change forcing many small edits across multiple places.&lt;/p&gt;

&lt;p&gt;I connected this to things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;model changes&lt;/li&gt;
&lt;li&gt;validation changes&lt;/li&gt;
&lt;li&gt;input flow changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The smell is present, but in this app I would rate it as &lt;strong&gt;medium&lt;/strong&gt;, not strong.&lt;/p&gt;

&lt;p&gt;The reason is contextual. The app is still small, so the edit points are visible and manageable. The conditions for shotgun surgery already exist, but they have not exploded yet. So the smell is real, but at this stage it looks more like a visible structural risk than the most dominant pain point.&lt;/p&gt;

&lt;p&gt;This was a useful reminder that smell severity always depends on context.&lt;/p&gt;

&lt;p&gt;Writer Note:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Shotgun surgery is related to fragile change points, but they are not the same. Fragile change points focus on how risky and unstable a change location already is, while shotgun surgery focuses on how widely one conceptual change must spread across the code. In this Todo app, the core functions are already fragile because they mix multiple responsibilities, which is why fragile change points can be rated strong. But the codebase is still small, so although one change may spread to several functions, that spread is still limited and manageable. That is why shotgun surgery is better rated as medium here, not strong.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Large function / mixed responsibility : strong
&lt;/h3&gt;

&lt;p&gt;This was one of the most important corrections for me.&lt;/p&gt;

&lt;p&gt;At first, I thought the functions were not very large by line count, so maybe the smell was only mild. But that turned out to be the wrong lens.&lt;br&gt;
The key lesson here was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;line count is not the main issue&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;responsibility load is&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Even though addTodo(), toggleTodo(), and removeTodo() are still short functions, they mix:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;output and prompting&lt;/li&gt;
&lt;li&gt;input&lt;/li&gt;
&lt;li&gt;validation&lt;/li&gt;
&lt;li&gt;mutation&lt;/li&gt;
&lt;li&gt;feedback&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the real smell is not “large function” in a superficial line-count sense. The real smell is mixed responsibility, and that is strongly present.&lt;/p&gt;

&lt;p&gt;This was an important calibration point for me, because it connects to a recurring weakness from Module 1 too: I sometimes underestimate mixed responsibility when the function is still short. After correcting that, I now rate this smell as &lt;strong&gt;strong&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  God object : weak / not really present
&lt;/h3&gt;

&lt;p&gt;I rated god object as weak or not really present.&lt;/p&gt;

&lt;p&gt;The reason is simple, this app is still too small to honestly call anything a god object. There is no single giant object that owns everything in a meaningful object-oriented sense. The bigger problems here are still poor boundaries and mixed responsibilities.&lt;/p&gt;
&lt;h3&gt;
  
  
  Feature envy : weak / not really present
&lt;/h3&gt;

&lt;p&gt;I also rated &lt;strong&gt;feature envy&lt;/strong&gt; as weak or not really present.&lt;/p&gt;

&lt;p&gt;Again, the reason is that this is not the main pattern in the current structure. The app is too small, and the code is not interacting in a way that makes feature envy the most useful label.&lt;/p&gt;


&lt;h1&gt;
  
  
  Create Refactoring Plan
&lt;/h1&gt;

&lt;p&gt;After identifying the smells and their severity, we can start creating a refactoring plan based on which problems matter most. Let me ask you first: what do you think should be the first priority? Try answering that in your own mind, along with the reasoning behind it.&lt;/p&gt;

&lt;p&gt;For me, the first priority is &lt;strong&gt;poor boundaries&lt;/strong&gt;. The reason is that this is the smell most closely connected to the deeper structural problem in the Todo app. Input, output, state mutation, and business logic are still too mixed together. As long as those boundaries remain unclear, other problems like fragile change points and duplication will keep appearing more easily.&lt;/p&gt;

&lt;p&gt;Because of that, my first refactor direction was to create a store that holds state and business logic.&lt;/p&gt;

&lt;p&gt;The idea is to introduce something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;TodoStore&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;TodoItem&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;TodoItem&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;todos&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throws&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;toggleTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="nv"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throws&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;removeTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="nv"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throws&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;TodoItem&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 felt like a good first move because it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;gives state a clearer owner&lt;/li&gt;
&lt;li&gt;centralizes mutation&lt;/li&gt;
&lt;li&gt;separates todo-related logic from input/output&lt;/li&gt;
&lt;li&gt;reduces ripple effects&lt;/li&gt;
&lt;li&gt;makes future changes safer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So this became the first refactor direction I chose.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to refactor?
&lt;/h2&gt;

&lt;p&gt;If this part feels a little jumpy because I suddenly introduced TodoStore and showed the code without walking through the full refactoring process, that is completely fair.&lt;/p&gt;

&lt;p&gt;I will cover the actual refactoring process in the next article of this series (&lt;a href="https://dev.to/cakoko/series/37430"&gt;What I’m Learning About Writing Better Structured Code&lt;/a&gt;). The next article is &lt;strong&gt;“Guide to Solve the Problem: Introduction to SOLID Principles.”&lt;/strong&gt; In that article, I will focus more on how I gradually moved from the original Todo app into a structure with clearer ownership, better boundaries, and more focused responsibilities, using the SOLID principles as a guide.&lt;/p&gt;




&lt;h1&gt;
  
  
  From the Writer
&lt;/h1&gt;

&lt;p&gt;Hello, allow me to introduce myself. I’m Cakoko. We’ve reached the end of this article, and I sincerely thank you for taking the time to read it.&lt;/p&gt;

&lt;p&gt;If you have any questions or feedback, feel free to reach out to me directly via email at &lt;a href="mailto:cakoko.dev@gmail.com"&gt;cakoko.dev@gmail.com&lt;/a&gt;. I’m more than happy to hear your thoughts, whether they are about my English writing, the technical ideas in this article, or anything I may have misunderstood. Your feedback will help me grow.&lt;/p&gt;

&lt;p&gt;I look forward to connecting with you in future articles. Btw, I’m a mobile developer, final year Computer Science student, and an Apple Developer Academy @ IL graduate. I’m also open to various opportunities such as collaborations, internships, or full-time positions. It would make me very happy to explore those possibilities.&lt;/p&gt;

&lt;p&gt;Until next time, stay curious and keep learning.&lt;/p&gt;




&lt;h1&gt;
  
  
  Open for Feedback
&lt;/h1&gt;

&lt;p&gt;This article is part of my personal learning journey. It may not be completely accurate or perfect, and that is okay. I’m sharing what I’ve learned so far in the hope that it can also help others who are exploring similar topics.&lt;/p&gt;

&lt;p&gt;If you have any feedback, suggestions, or corrections, I would truly appreciate them. I’m always open to learning more and improving along the way.&lt;/p&gt;

</description>
      <category>softwaredevelopment</category>
      <category>architecture</category>
      <category>swift</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Seeing the problem: An Introduction to Separation of Concerns</title>
      <dc:creator>cakoko</dc:creator>
      <pubDate>Tue, 24 Mar 2026 04:14:08 +0000</pubDate>
      <link>https://dev.to/cakoko/seeing-the-problem-an-introduction-to-separation-of-concerns-4l1</link>
      <guid>https://dev.to/cakoko/seeing-the-problem-an-introduction-to-separation-of-concerns-4l1</guid>
      <description>&lt;p&gt;Separation of concerns is one of the first topics that comes up when we want to move from writing code that simply works to writing code that is structured well. At first, I thought it only meant splitting code into smaller functions so it looked cleaner, was easier to reuse, and had less duplication. If a function looked too long, I would cut some lines out and move them into another function. If a piece of code looked too complex, I would do the same.&lt;/p&gt;

&lt;p&gt;For years, I wrote code mostly based on instinct and personal preference without really asking whether there was a better way to think about structure. I did not seriously question where a responsibility should live, what a function should truly own, or whether splitting code actually improved the design. Then I started my journey in LUR to rethink the way I write code, and relearning separation of concerns became one of the first things I focused on. From that learning process, i made an article series &lt;a href="https://dev.to/cakoko/what-im-learning-about-writing-better-structured-code-a-learning-series-k5l"&gt;”What I’m Learning About Writing Better Structured Code.”&lt;/a&gt; and this is the first part.&lt;/p&gt;




&lt;h1&gt;
  
  
  Seeing the Problem
&lt;/h1&gt;

&lt;p&gt;Working code is not the same as well-structured code. A program can work correctly for the current problem, current input, and current environment, yet still become difficult to change later. As the app grows, the code also needs to grow without creating unnecessary friction for the programmer or the system itself.&lt;/p&gt;

&lt;p&gt;To see that difference more clearly, let’s look at a small Todo CLI app. At first glance, nothing seems obviously wrong with it. It runs, it accepts input, and it performs the features it promises. But if we look past the result and inspect the structure, we can start to see the real problem.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Foundation&lt;/span&gt;

&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;TodoItem&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"String"&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;isDone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;TodoItem&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;showMenu&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"=== Todo App ==="&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"1. Show all todos"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2. Add todo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"3. Toggle todo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"4. Remove todo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"5. Exit"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Choose:"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;showTodos&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="n"&gt;todos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isEmpty&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"No todos yet."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enumerated&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;mark&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isDone&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"[x]"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"[ ]"&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;. &lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;mark&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;"&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;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Enter todo title: "&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="s"&gt;")"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;readLine&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trimmingCharacters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;in&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;whitespaces&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isEmpty&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;TodoItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"input, isDone: false))"&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Todo added."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invalid title."&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;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;toggleTodo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;showTodos&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isEmpty&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Enter todo number to toggle:"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;readLine&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;index&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="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isDone&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Todo updated."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invalid number."&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;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Please enter a valid number."&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;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;removeTodo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;showTodos&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isEmpty&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Enter todo number to remove:"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;readLine&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;index&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="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;removed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Removed: &lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;removed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&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;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invalid number."&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;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Please enter a valid number."&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;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;runApp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;isRunning&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;isRunning&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;showMenu&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;choice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;readLine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;

        &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;showTodos&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"3"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;toggleTodo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"4"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;removeTodo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"5"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;isRunning&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Goodbye."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unknown option."&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;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;runApp&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, this code looked fine to me because it worked. But the point of the lesson was not just to check whether the program ran. The real goal was to inspect the structure behind it.&lt;/p&gt;

&lt;p&gt;If you cannot spot the problem yet, that is completely fine. I also did not see it clearly at first. In the next part, I will break down what separation of concerns actually means and the lens I started using to notice where the boundaries in this code were mixed.&lt;/p&gt;




&lt;h1&gt;
  
  
  Introduction to Separation of Concern
&lt;/h1&gt;

&lt;p&gt;Separation of concerns simply means different parts of a program should handle different responsibilities. Instead of letting one place do everything, we try to give each part a clearer job. This becomes important as an app grows, because code that feels manageable at the beginning can quickly become hard to understand and harder to change.&lt;/p&gt;

&lt;p&gt;When responsibilities are mixed together, one part of the program starts doing too many things at once. A small change in one feature can affect unrelated code. It becomes harder to tell where a piece of logic should live. Input and output, state changes, business rules, and app flow can all end up too close to each other. The code may still work, but its structure starts resisting change.&lt;/p&gt;

&lt;p&gt;So the goal of separation of concerns is not to split everything into tiny pieces just for the sake of cleanliness. The real goal is to make responsibilities easier to understand, easier to change, and easier to reason about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Supporting Concepts
&lt;/h2&gt;

&lt;p&gt;To understand separation of concerns more clearly, we also need to look at several related concepts in software engineering. They help us see what separation of concerns is really trying to achieve, why mixed responsibilities become a problem, and what better boundaries can look like in code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Responsibility ownership
&lt;/h3&gt;

&lt;p&gt;Each part of the code should have a clearer job. For example, displaying things should mainly focus on displaying. State mutation should have a clearer owner. Input handling should not also become the place where all business rules live.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cohesion
&lt;/h3&gt;

&lt;p&gt;Things that belong together should stay together. A function can still do several small steps, but those steps should feel like one meaningful responsibility rather than unrelated tasks grouped in the same place.&lt;/p&gt;

&lt;h3&gt;
  
  
  Coupling
&lt;/h3&gt;

&lt;p&gt;Different parts of the code should not know too much about each other. If everything can directly touch everything else, the code becomes harder to understand, change, and grow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Modularity
&lt;/h3&gt;

&lt;p&gt;Modularity means breaking a system into understandable pieces. This does not mean creating many files just for style. It means organizing code into parts that can be understood and changed more independently.&lt;/p&gt;

&lt;h3&gt;
  
  
  Abstraction
&lt;/h3&gt;

&lt;p&gt;Abstraction means hiding detail behind a simpler and more meaningful idea. For example, toggleTodo(at:) is a more useful abstraction than repeating low-level mutation logic in many places.&lt;/p&gt;

&lt;h3&gt;
  
  
  Boundaries
&lt;/h3&gt;

&lt;p&gt;Boundaries are the lines between different responsibilities.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;data&lt;/li&gt;
&lt;li&gt;state&lt;/li&gt;
&lt;li&gt;input/output&lt;/li&gt;
&lt;li&gt;business logic&lt;/li&gt;
&lt;li&gt;flow control&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these concepts are useful as foundational knowledge for writing well-structured code, especially when trying to notice and apply separation of concerns. They are also closely connected. Responsibility ownership helps us ask which part of the code should own a job. Cohesion helps us check whether related work stays together. Coupling helps us see when different parts of the code depend on each other too much. Modularity and abstraction help organize those responsibilities into clearer pieces. Boundaries tie all of these ideas together by showing where one responsibility should stop and another should begin.&lt;/p&gt;

&lt;p&gt;From my learning journey, the idea of boundaries was the most practical lens for noticing whether code was applying separation of concerns well. It helped me see how responsibilities were placed, where they were mixed, and where the structure started to become unclear. So in the next part, I will use boundaries as the main lens to inspect the legacy code above, while still supported by the other concepts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading the Todo App Through Boundaries
&lt;/h2&gt;

&lt;p&gt;Now that we have introduced boundaries as a lens, we can use them to inspect whether the Todo app mixes responsibilities that leads to poor separation of concern. &lt;/p&gt;

&lt;p&gt;For example, in &lt;code&gt;addTodo()&lt;/code&gt;, we can already see several different responsibilities living in the same place: user input, todo-related logic (business logic), state mutation, and output rendering. The function does not only add a todo. It also asks for input, validates it, updates shared state, and prints feedback to the user.&lt;/p&gt;

&lt;p&gt;Without looking at the next code, try to copy the code and paste to your code editor then mark it based on the data, state, input/output, business logic and flow control.&lt;/p&gt;

&lt;p&gt;From my perspective, i found this&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;TodoItem&lt;/code&gt; → data model&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;todos&lt;/code&gt; → app state&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;showMenu()&lt;/code&gt; → output rendering&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;showTodos()&lt;/code&gt; → output rendering&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;addTodo()&lt;/code&gt; → user input + todo operations (business logic) + output rendering&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;toggleTodo()&lt;/code&gt; → user input + todo operations  (business logic) + output rendering&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;removeTodo()&lt;/code&gt; → user input + todo operations  (business logic) + output rendering&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;runApp()&lt;/code&gt; → app flow / menu loop + some user input&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you can see from the code above, several functions contain more than one kind of responsibility. That means the boundaries between responsibilities are blurred, and this is where mixed responsibilities start to appear. Because of that, the code does not follow separation of concerns very well.&lt;/p&gt;

&lt;p&gt;The problem is not that the app has input, output, state, and logic. Of course it needs all of them. The problem is that too many of them are handled in the same place. When one function reads input, validates it, changes state, applies logic, and renders output at the same time, its responsibility becomes less clear.&lt;/p&gt;

&lt;h2&gt;
  
  
  The mental model
&lt;/h2&gt;

&lt;p&gt;After noticing that, try asking yourself this question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;If this app changes, what kind of change should force this piece of code to change?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That question helps reveal the real point of separation of concerns. A well-structured piece of code should ideally change for one clear reason, not for many unrelated ones. If a single function has to change because of UI changes, input changes, validation changes, and business rule changes all at once, that is usually a sign that too many concerns are mixed together.&lt;/p&gt;

&lt;p&gt;For example, imagine I want to change how input is taken. Maybe today the app uses &lt;code&gt;readLine()&lt;/code&gt;, but later I want input to come from another interface. Functions like &lt;code&gt;addTodo()&lt;/code&gt;, &lt;code&gt;toggleTodo()&lt;/code&gt;, and &lt;code&gt;removeTodo()&lt;/code&gt; would all need to change, because input handling is mixed directly into them. The same thing happens if I want to change how messages are shown to the user. Since those functions also print feedback directly, a display change would again force the same functions to change. Even a change in validation rules or todo behavior could affect those same places. One function ends up changing for multiple unrelated reasons, and that is the main sign that the boundaries are not clear enough.&lt;/p&gt;

&lt;p&gt;So the point of separation of concerns is not just to split code into smaller pieces. The real point is to place responsibilities in clearer boundaries, so each part of the code has a more focused job and a more understandable reason to change.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Noticing to Separating
&lt;/h2&gt;

&lt;p&gt;After noticing code that does not apply separation of concerns well and understanding the pain it can create, the next question becomes: &lt;strong&gt;how should we start improving it?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This was the part that felt the most subjective to me. Even after seeing that responsibilities were mixed, I still did not know how to judge whether something should actually be separated. The hard part was that not everything inside a function deserves to become its own unit. Some parts are truly separate concerns, while others are just details of the same responsibility.&lt;/p&gt;

&lt;p&gt;Then i found the most useful principle after looking to a lot of articles:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A separate concern is something that can change independently.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;A detail is something that usually changes together with its parent responsibility.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In practical terms, this means I should not extract something just because it exists as a smaller step inside a function. I should ask whether that part could realistically need to change on its own.&lt;/p&gt;

&lt;p&gt;For example, in &lt;code&gt;addTodo()&lt;/code&gt;, input handling could change independently if the app later stops using &lt;code&gt;readLine()&lt;/code&gt;. Validation could also change independently if the app starts enforcing stricter rules for todo titles. Output rendering could change independently if the way feedback is shown to the user changes. These are good signs that input handling, validation, and output rendering may deserve clearer boundaries instead of all living inside the same function.&lt;/p&gt;

&lt;p&gt;But this judgment depends on the level we are looking at. A concern can be separate at one level, while still containing smaller details that should stay inside it. For example, in &lt;code&gt;showTodos()&lt;/code&gt;, the line that chooses between &lt;code&gt;"[x]"&lt;/code&gt; and &lt;code&gt;"[ ]"&lt;/code&gt; is still part of the rendering job. It is a formatting detail inside rendering, not automatically a separate concern by itself. If the display style changes, that line would likely change together with the rest of the rendering logic.&lt;/p&gt;

&lt;p&gt;That changed the question completely. I stopped asking only whether a function looked long or complicated. The better question became:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is this part likely to evolve on its own?&lt;/li&gt;
&lt;li&gt;Or is it just one small detail of the function’s main job?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From there, the most useful rule I learned was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Extract only when the thing being extracted has its own meaningful reason to change.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Before this, I used to think about structure with a much simpler rule:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;1 function = 1 purpose&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But over time, I realized that the better version is this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A function should have one cohesive responsibility at the level of abstraction it owns.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This idea was very important for me, because it helped me stop treating every small detail as a separate concern.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Noticing to Separating, then Overthinking it
&lt;/h2&gt;

&lt;p&gt;This new way of thinking also triggered a lot of overthinking for me. By this point, I would not be surprised if some readers are also starting to feel the same thing.&lt;/p&gt;

&lt;p&gt;Most of that overthinking came from doubting my own judgment. In the example above, we are trying to think about how the code might need to change in the future. But once I started thinking in terms of future changes, the possibilities felt endless. What if the input changes? What if the output changes? What if validation becomes more complex? What if the app grows in another direction?&lt;/p&gt;

&lt;p&gt;That is where my mind started branching too much. If every possible future scenario can be used as a reason to separate something, then it becomes easy to overthink the design and lose confidence in my own judgment.&lt;/p&gt;

&lt;p&gt;Then I remembered something I once asked my Apple Developer Academy mentor, Gerson. His answer was&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Your code is growing. Let it grow based on the need.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I understood that as a reminder that I do not need to predict the perfect architecture too early. Looking back, I think I had started to treat future change as something dangerous, as if needing to refactor later automatically meant I had failed (validating the overthinking above).&lt;/p&gt;

&lt;p&gt;After that, I read more about this idea and realized the healthier way to think about it is much simpler:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;write the simplest code that solves the current problem&lt;/li&gt;
&lt;li&gt;let the code grow with real requirements&lt;/li&gt;
&lt;li&gt;refactor when the current shape starts resisting change&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Refactoring later is &lt;strong&gt;not evil&lt;/strong&gt;. It is a normal part of software development.&lt;/p&gt;

&lt;p&gt;This connects with ideas like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;make it work&lt;/li&gt;
&lt;li&gt;make it clear&lt;/li&gt;
&lt;li&gt;make it flexible&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also connects with the YAGNI principle, short for &lt;strong&gt;You Aren’t Gonna Need It&lt;/strong&gt;. The core idea is that we should not build for imagined needs too early. If you want to read full explanation, here is the &lt;a href="https://www.geeksforgeeks.org/software-engineering/what-is-yagni-principle-you-arent-gonna-need-it/" rel="noopener noreferrer"&gt;article&lt;/a&gt;  .&lt;/p&gt;

&lt;p&gt;A better approach is to solve the current problem clearly, then improve the structure when real change starts to demand it. That means a design is not automatically bad just because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it is simple&lt;/li&gt;
&lt;li&gt;it is unfinished&lt;/li&gt;
&lt;li&gt;it may need refactoring later&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A design becomes bad when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the problem is still small&lt;/li&gt;
&lt;li&gt;but the code is already confusing&lt;/li&gt;
&lt;li&gt;even small changes already feel messy&lt;/li&gt;
&lt;li&gt;responsibility ownership is unclear right now&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the best way to think about it is not:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;right design&lt;/li&gt;
&lt;li&gt;wrong design&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;but rather:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;good enough for now&lt;/li&gt;
&lt;li&gt;starting to strain&lt;/li&gt;
&lt;li&gt;needs refactor soon&lt;/li&gt;
&lt;li&gt;too rigid and harmful&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Todo app above is currently in the &lt;strong&gt;“good enough for now, but future strain is visible”&lt;/strong&gt; stage. If you want to go deeper into how to judge code like this, I wrote the next article, &lt;strong&gt;“Judging the Problem: Code Smells and Refactoring”&lt;/strong&gt; which is Part 2 of my series, &lt;a href="https://dev.to/cakoko/what-im-learning-about-writing-better-structured-code-a-learning-series-k5l"&gt;”What I’m Learning About Writing Better Structured Code.”&lt;/a&gt;. It will be out soon, so if this topic interests you, feel free to follow me and stay tuned for the next part.&lt;/p&gt;




&lt;h1&gt;
  
  
  From the Writer
&lt;/h1&gt;

&lt;p&gt;Hello, allow me to introduce myself. I’m Cakoko. We’ve reached the end of this article, and I sincerely thank you for taking the time to read it.&lt;/p&gt;

&lt;p&gt;If you have any questions or feedback, feel free to reach out to me directly via email at &lt;a href="mailto:cakoko.dev@gmail.com"&gt;cakoko.dev@gmail.com&lt;/a&gt;. I’m more than happy to hear your thoughts, whether they are about my English writing, the technical ideas in this article, or anything I may have misunderstood. Your feedback will help me grow.&lt;/p&gt;

&lt;p&gt;I look forward to connecting with you in future articles. Btw, I’m a mobile developer, final year Computer Science student, and an Apple Developer Academy @ IL graduate. I’m also open to various opportunities such as collaborations, internships, or full-time positions. It would make me very happy to explore those possibilities.&lt;/p&gt;

&lt;p&gt;Until next time, stay curious and keep learning.&lt;/p&gt;




&lt;h1&gt;
  
  
  Open for Feedback
&lt;/h1&gt;

&lt;p&gt;This article is part of my personal learning journey. It may not be completely accurate or perfect, and that is okay. I’m sharing what I’ve learned so far in the hope that it can also help others who are exploring similar topics.&lt;/p&gt;

&lt;p&gt;If you have any feedback, suggestions, or corrections, I would truly appreciate them. I’m always open to learning more and improving along the way.&lt;/p&gt;

</description>
      <category>softwaredevelopment</category>
      <category>architecture</category>
      <category>swift</category>
      <category>mobile</category>
    </item>
    <item>
      <title>What I’m Learning About Writing Better Structured Code: A Learning Series</title>
      <dc:creator>cakoko</dc:creator>
      <pubDate>Tue, 24 Mar 2026 04:03:21 +0000</pubDate>
      <link>https://dev.to/cakoko/what-im-learning-about-writing-better-structured-code-a-learning-series-k5l</link>
      <guid>https://dev.to/cakoko/what-im-learning-about-writing-better-structured-code-a-learning-series-k5l</guid>
      <description>&lt;p&gt;After working as a mobile developer and iOS developer for more than two years, I started seeing the same kind of problems appear again and again. Many of them came from code that worked at the time it was written, but was not structured well enough to grow without causing pain later.&lt;/p&gt;

&lt;p&gt;I made those mistakes myself. My coworkers made them too. Sometimes the problems came from older code written by people before us. Whether we realize it or not.&lt;/p&gt;

&lt;p&gt;Code with weak structure eventually creates friction. Small changes become harder than they should be. Responsibilities become unclear. And over time, working with the code starts to feel heavier. That experience pushed me to think more seriously about how to avoid falling into the same problems again. It also made me question my own skill in writing code. I started realizing that I had learned how to make things work, but I had not yet learned broadly, deeply, or systematically enough how to think like a software developer when it comes to structure and design.&lt;/p&gt;

&lt;p&gt;That realization became the start of a personal learning journey. The concept that i use is &lt;strong&gt;LUR: Learn, Unlearn, Re-Learn&lt;/strong&gt;. It is a mindset I was introduced to during my time at Apple Developer Academy back then. For me, it means going back to important topics, questioning what I think I already understand, and rebuilding that understanding in a better way.&lt;/p&gt;

&lt;p&gt;The first output of this learning journey is an article series called "What I’m Learning About Writing Better Structured Code". At least until now, the draft of this is 3 article which is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Seeing the problem: An Introduction to Separation of Concern. PUBLISHED at &lt;a href="https://dev.to/cakoko/seeing-the-problem-an-introduction-to-separation-of-concerns-4l1"&gt;https://dev.to/cakoko/seeing-the-problem-an-introduction-to-separation-of-concerns-4l1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Judging the Problem: Code Smells and Refactoring&lt;/li&gt;
&lt;li&gt;Guide to Solve the Problem: Introduction to SOLID Principles&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I will be publishing the articles here on dev.to as I finish them. If this journey sounds interesting to you, feel free to follow me and stay tuned for the next parts.&lt;/p&gt;

</description>
      <category>softwaredevelopment</category>
      <category>career</category>
      <category>swift</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
