<?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: Michał Cichoń</title>
    <description>The latest articles on DEV Community by Michał Cichoń (@michal_cichon).</description>
    <link>https://dev.to/michal_cichon</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%2F31831%2Feac80534-bdb3-4fde-81d1-12e7896d08f6.png</url>
      <title>DEV Community: Michał Cichoń</title>
      <link>https://dev.to/michal_cichon</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/michal_cichon"/>
    <language>en</language>
    <item>
      <title>Effective Code Reviews: Habits That Make Teams Stronger</title>
      <dc:creator>Michał Cichoń</dc:creator>
      <pubDate>Mon, 15 Dec 2025 09:43:35 +0000</pubDate>
      <link>https://dev.to/michal_cichon/effective-code-reviews-habits-that-make-teams-stronger-3m1d</link>
      <guid>https://dev.to/michal_cichon/effective-code-reviews-habits-that-make-teams-stronger-3m1d</guid>
      <description>&lt;p&gt;&lt;strong&gt;A healthy code review culture can transform a team. It improves code quality, spreads knowledge, and helps developers grow. But good code reviews don’t just happen. They come from habits, collaboration, and curiosity. Today I would like to share a few practical guidelines that can make your team’s review process smoother, more meaningful, and far more effective.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Don’t Be Afraid to Ask Questions
&lt;/h2&gt;

&lt;p&gt;One of the biggest misconceptions about code review is that you’re expected to know everything. In reality, questions are often the most valuable part of a review. Ask questions whenever something isn’t clear. If a piece of logic seems surprising or if you’re not sure why a particular pattern was chosen, it's always better to ask a question then simply move on silently.&lt;/p&gt;

&lt;p&gt;Questions help you understand the change, push the author to clarify, and sometimes reveal areas where the code or documentation could be improved. Curiosity strengthens the team more than silent approval ever could.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prioritize Code Reviews Over Your Own Tasks
&lt;/h2&gt;

&lt;p&gt;We all want to get things done. That's why it’s always tempting to rather focus on your own work than your peer's. In reality, tasks waiting for code review are closer to being finished than the tasks you’re just starting and they usually should have higher priority.&lt;/p&gt;

&lt;p&gt;By prioritizing code reviews, you help keep the development pipeline flowing. Your peer can merge their work, move forward, and unblock other parts of the system. It’s a small shift in priority that dramatically increases team throughput.&lt;/p&gt;

&lt;p&gt;I think it's really important to keep in mind. When we stop reviewing our peers code, we simply end up with a high pile or pull requests waiting for being merged. It usually leads to more merge conflicts, more stress just before the release, and more headaches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use a Checklist to Stay Consistent
&lt;/h2&gt;

&lt;p&gt;Even experienced developers miss things. That's why a checklist can help ensure consistency and reduces mental load. A good review checklist might include things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Are there unit tests for the new or changed functionality?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Does the code follow the project’s style guide?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Are there potential race conditions or concurrency issues?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Is caching used correctly, or could it cause stale data or memory bloat?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Are there strong reference cycles?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Are all new strings localized properly? Maybe some strings are still missing? Did we make sure someone from Product/Marketing will take care of it?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Maybe we have more external dependencies in our team we should keep in mind before we go live with the new changes?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Are accessibility identifiers added where needed?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A checklist makes the review process thorough without being overwhelming. Usually it makes sense to share a short checklist with the team and adopt it as a regular step in your development process. But nothing stops you from having your own, more detailed list if you like so.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep Pull Requests Small
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fav64alf6v5fy7gxsrmq0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fav64alf6v5fy7gxsrmq0.jpg" alt="Big pull request" width="800" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Huge pull requests are stressful. Both to write and to review. They slow everything down and make it hard to spot issues. Reviewing bigger pull request takes more time, and it's way more challenging from the start.&lt;/p&gt;

&lt;p&gt;Whenever possible, work iteratively. Break larger tasks into smaller PRs: setup, core logic, UI layer, tests, cleanup, etc. Small PRs are easier to understand, faster to review, and far less likely to introduce bugs.&lt;/p&gt;

&lt;p&gt;When we work on feature branches, and we see the feature we're working on is simply too big for a single pull request, it might make sense to open a base branch where we point our iterations. Then when the whole feature is done under the base feature branch (so we merged Part 1 and Part 2), we can create a new combined pull request pointing &lt;code&gt;develop&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;develop
   |
   +--- XYZ-123-My-New-Feature-Base
         |
         +--- XYZ-123-My-New-Feature-Part-1
         |
         +--- XYZ-123-My-New-Feature-Part-2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Avoid “LGTM” Without Any Comments
&lt;/h2&gt;

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

&lt;p&gt;Sometimes a PR really does look perfect. But adding only a “Looks Good To Me” comment can feel impersonal and unhelpful.&lt;/p&gt;

&lt;p&gt;Instead, highlight something you genuinely liked: a clean abstraction, a thoughtful test, a well-documented function.&lt;/p&gt;

&lt;p&gt;Positive feedback matters. It reinforces good patterns, encourages craftsmanship, and makes code reviews feel like collaboration rather than gatekeeping.&lt;/p&gt;

&lt;h2&gt;
  
  
  Be Curious
&lt;/h2&gt;

&lt;p&gt;If you’re not sure where to start with a review, that’s okay. Use your checklist. Ask the author to walk you through the changes. Start with the high-level overview, and then go deeper. &lt;/p&gt;

&lt;p&gt;Curiosity creates learning opportunities. Code review is one of the best ways to grow your skills because it exposes you to patterns, ideas, and solutions you wouldn’t encounter on your own. Being curious is not a weakness but actually it’s one of the strongest habits you can develop as an engineer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Code review isn’t just about finding bugs. It’s about building a culture of clarity, collaboration, and continuous improvement.&lt;/p&gt;

&lt;p&gt;By asking questions, keeping PRs small, using a checklist, giving meaningful feedback, and supporting one another your team can turn the review process into a powerful engine for quality and growth.&lt;/p&gt;

&lt;p&gt;Strong code reviews make strong teams. And strong teams ship better software.&lt;/p&gt;




&lt;p&gt;This article was originally published on my blog: &lt;a href="https://michalcichon.github.io/software-development/2025/12/12/effective-code-reviews-habits-that-make-teams-stronger.html" rel="noopener noreferrer"&gt;https://michalcichon.github.io/software-development/2025/12/12/effective-code-reviews-habits-that-make-teams-stronger.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you enjoyed this post, check out my other articles on software development, teamwork, and best practices for building better code.&lt;/p&gt;

</description>
      <category>codereview</category>
      <category>bestpractices</category>
      <category>softwaredevelopment</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Dependency injection patterns in Swift</title>
      <dc:creator>Michał Cichoń</dc:creator>
      <pubDate>Wed, 26 Nov 2025 10:52:00 +0000</pubDate>
      <link>https://dev.to/michal_cichon/dependency-injection-patterns-in-swift-4lbc</link>
      <guid>https://dev.to/michal_cichon/dependency-injection-patterns-in-swift-4lbc</guid>
      <description>&lt;p&gt;&lt;strong&gt;One of the most useful patterns in software development — and one that is available in many languages, not just Swift — is dependency injection. When I first learned about it over a decade ago, I started using it everywhere possible. It’s a simple idea with a surprisingly big impact. Here’s why it’s worth mastering.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is it so useful?
&lt;/h2&gt;

&lt;p&gt;One of the biggest advantages of using dependency injection is that it makes our code immediately more testable. Even in projects full of singletons, we can break apart large chunks of tightly coupled code into smaller, more loosely coupled components. Our implementations rely more on protocols (interfaces) rather than concrete classes, making it easier to swap or replace them.&lt;/p&gt;

&lt;p&gt;By depending on abstractions rather than concrete implementations, each component can focus solely on its own responsibilities, without knowing the details of how other parts of the system are managed. This makes it easier to reuse components across different parts of an app. We can easily replace services with mocks during testing or switch implementations when requirements change.&lt;/p&gt;

&lt;h2&gt;
  
  
  A basic example of how to use initializer-based dependency injection
&lt;/h2&gt;

&lt;p&gt;Imagine that we use in our &lt;code&gt;ViewModel&lt;/code&gt;, a couple of services which are singletons. Instead of referencing them directly in the method definition 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;func&lt;/span&gt; &lt;span class="nf"&gt;makeUserHappy&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;currentUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;UserService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;currentUser&lt;/span&gt;
    &lt;span class="kt"&gt;PremiumService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;grantSuperPowers&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;currentUser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kt"&gt;AdService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeAds&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;We can move references outside of the method:&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;ExampleViewModel&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;userService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;UserService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&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;premiumService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;PremiumService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&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;adService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;AdService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;makeUserHappy&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;currentUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;userService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;currentUser&lt;/span&gt;
        &lt;span class="n"&gt;premiumService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;grantSuperPowers&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;currentUser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;adService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeAds&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 next step of moving it away would be to pass these references to the initializer:&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;ExampleViewModel&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;userService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UserService&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;premiumService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PremiumService&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;adService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AdService&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;userService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UserService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
         &lt;span class="nv"&gt;premiumService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PremiumService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nv"&gt;adService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AdService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&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;userService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;userService&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;premiumService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;premiumService&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;adService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;adService&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;makeUserHappy&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;currentUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;userService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;currentUser&lt;/span&gt;
        &lt;span class="n"&gt;premiumService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;grantSuperPowers&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;currentUser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;adService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeAds&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;It’s already looks much better. Now we can create mock variants of these classes for our tests using inheritance 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;MockUser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UserService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;currentUser&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="kt"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Bob"&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;class&lt;/span&gt; &lt;span class="kt"&gt;MockPremiumService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PremiumService&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;superUsers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;grantSuperPowers&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="kt"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;powers&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;premiumPowers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;superUsers&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="n"&gt;user&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;Inheritance can help us replace the implementation of exposed service methods, but the rest of the underlying logic remains the same. This can lead to serious side effects, especially if the internal logic of our service classes is complex. A much better approach is to rely on protocol conformance instead of concrete classes in our ViewModel. &lt;/p&gt;

&lt;p&gt;Taking this into consideration, we can end up with 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;class&lt;/span&gt; &lt;span class="kt"&gt;ExampleViewModel&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;userService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UserServiceProtocol&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;premiumService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PremiumServiceProtocol&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;adService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AdServiceProtocol&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;userService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UserServiceProtocol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;UserService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
         &lt;span class="nv"&gt;premiumService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PremiumServiceProtocol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;PremiumService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nv"&gt;adService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AdServiceProtocol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;AdService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&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;userService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;userService&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;premiumService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;premiumService&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;adService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;adService&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;makeUserHappy&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;currentUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;userService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;currentUser&lt;/span&gt;
        &lt;span class="n"&gt;premiumService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;grantSuperPowers&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;currentUser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;adService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeAds&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;Then we can implement &lt;code&gt;MockUserService&lt;/code&gt; and the real &lt;code&gt;UserService&lt;/code&gt; independently, without needing to mix parts of the real service into the mock — which is exactly what tends to happen when relying on inheritance.&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="c1"&gt;// Protocols&lt;/span&gt;

&lt;span class="kd"&gt;protocol&lt;/span&gt; &lt;span class="kt"&gt;UserServiceProtocol&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;currentUser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;User&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;fetchUserData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;protocol&lt;/span&gt; &lt;span class="kt"&gt;PremiumServiceProtocol&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;grantSuperPowers&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;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;User&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;purchase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;product&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Real implementations&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UserServiceProtocol&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;currentUser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;persistence&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;currentUser&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;fetchUserData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;async&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;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/user/me"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;persistence&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;saveUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="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;class&lt;/span&gt; &lt;span class="kt"&gt;PremiumService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PremiumServiceProtocol&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;grantSuperPowers&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;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;User&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;updated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
        &lt;span class="n"&gt;updated&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isPremium&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;saveUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;updated&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;purchase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;product&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Product&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="p"&gt;{&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;storeKit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startPurchase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;product&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="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Mock implementations&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;MockUserService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UserServiceProtocol&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;currentUser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"test-id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"TestUser"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;isPremium&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="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;fetchUserDataCalled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;fetchUserData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fetchUserDataCalled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="c1"&gt;// Instant predictable mock result&lt;/span&gt;
        &lt;span class="n"&gt;currentUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"mock-123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"MockUser"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;isPremium&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;class&lt;/span&gt; &lt;span class="kt"&gt;MockPremiumService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PremiumServiceProtocol&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;grantedUsers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;User&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;purchasedProducts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Product&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;grantSuperPowers&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;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;grantedUsers&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="n"&gt;user&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;purchase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;product&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;purchasedProducts&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="n"&gt;product&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;What we have here is called &lt;strong&gt;initializer-based dependency injection&lt;/strong&gt; because we pass our dependencies through the initializer. In my opinion, it’s the best basic way to implement dependency injection for iOS applications. It’s a simple concept that anyone can learn quickly. It offers excellent testability: all dependencies are injected at object creation, so tests can consistently provide mocks or stubs. There’s no risk of uninitialized dependencies, which makes tests more reliable.&lt;/p&gt;

&lt;p&gt;It’s also characterized by strong modularity. Components are fully decoupled from concrete implementations, and each dependency is clearly defined in the initializer, making it easy to swap implementations.&lt;/p&gt;

&lt;p&gt;However, there are several other ways to handle dependency injection, which I will briefly explain in the next sections.&lt;/p&gt;

&lt;h2&gt;
  
  
  Property injection
&lt;/h2&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;ExampleViewModel&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// Dependencies are optional until injected&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;userService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UserServiceProtocol&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;premiumService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PremiumServiceProtocol&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;adService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AdServiceProtocol&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;makeUserHappy&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;currentUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;userService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;currentUser&lt;/span&gt;
        &lt;span class="n"&gt;premiumService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;grantSuperPowers&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;currentUser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;adService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeAds&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="c1"&gt;// Somewhere else &lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;vm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;ExampleViewModel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;userService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;UserService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;
&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;premiumService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;PremiumService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;
&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;adService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;AdService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This kind of dependency injection is usually seen in views containing IBOutlets, or in cases where we depend on view components that need to be initialized first. It’s more flexible because we don’t have to pass dependencies through the initializer—the injection timing is more flexible. However, this comes at a cost: there’s a risk of calling dependencies before they are injected, which can crash the app. It’s also harder to enforce correctness. That’s why I prefer the initializer-based dependency injection described in the previous section.&lt;/p&gt;

&lt;p&gt;It can be handy for testing, as we can easily swap dependencies after object creation, but it’s also easier to break and make our tests flaky.&lt;/p&gt;

&lt;p&gt;It’s not optimal in terms of modularity because objects rely on external code to inject dependencies before use. The contract of required dependencies is less explicit, making it easier to misuse the component.&lt;/p&gt;

&lt;p&gt;In my opinion, code clarity can also suffer because readers of our source code must track when and where dependencies are injected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method injection
&lt;/h2&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;ExampleViewModel&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;makeUserHappy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nv"&gt;userService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UserServiceProtocol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;premiumService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PremiumServiceProtocol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;adService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AdServiceProtocol&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;currentUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;userService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;currentUser&lt;/span&gt;
        &lt;span class="n"&gt;premiumService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;grantSuperPowers&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;currentUser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;adService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeAds&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="c1"&gt;// Usage:&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;vm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;ExampleViewModel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;makeUserHappy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nv"&gt;userService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UserService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;premiumService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PremiumService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;adService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AdService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This kind of dependency injection is more functional. We don’t need to store our dependencies as properties, and it’s also easy to test methods implemented this way. On the other hand, calling these methods can become quite verbose. It’s also not very optimal if we want to call the method from many different places. In that case, the problem of keeping everything consistent is simply delegated elsewhere, and we still need to manage it somewhere.&lt;/p&gt;

&lt;p&gt;It’s very testable. Each method call receives its dependencies explicitly, making it easy to test in isolation. However, it may take some extra effort to setup dependencies in our tests. &lt;/p&gt;

&lt;p&gt;In terms of modularity, we also can find a strong advantage of this approach. Objects don’t need to store dependencies as properties, reducing hidden state. But on the other hand, it can become cumbersome if multiple methods need the same dependencies repeatedly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;There are three basic approaches to dependency injection in Swift. What do you think about these approaches? Do you use dependency injection in your projects? Or maybe you use a dependency injection framework—if so, what do you think about these lightweight approaches?&lt;/p&gt;

&lt;p&gt;Regardless of the approach you choose, the key is to make your code more modular, testable, and maintainable. Even lightweight dependency injection can significantly improve the architecture of your Swift projects without adding unnecessary complexity. Experiment with different techniques and see which one fits your team and project style best.&lt;/p&gt;




&lt;p&gt;👉 &lt;em&gt;This post was originally published on my blog: &lt;a href="https://michalcichon.github.io/software-development/2025/11/25/dependency-injection-patterns-in-swift.html" rel="noopener noreferrer"&gt;https://michalcichon.github.io/software-development/2025/11/25/dependency-injection-patterns-in-swift.html&lt;/a&gt; where you can find more articles like this one.&lt;/em&gt; 🙂&lt;/p&gt;

</description>
      <category>swift</category>
      <category>programming</category>
      <category>ios</category>
    </item>
    <item>
      <title>Overengineered vs Underengineered Code: Finding the Balance</title>
      <dc:creator>Michał Cichoń</dc:creator>
      <pubDate>Sat, 11 Oct 2025 21:48:00 +0000</pubDate>
      <link>https://dev.to/michal_cichon/overengineered-vs-underengineered-code-finding-the-balance-n20</link>
      <guid>https://dev.to/michal_cichon/overengineered-vs-underengineered-code-finding-the-balance-n20</guid>
      <description>&lt;p&gt;We start with a simple idea — just a few lines of code. A few weeks later, someone adds a new feature, and suddenly that “temporary” solution you wrote on a Friday evening has become a critical dependency. Sounds familiar?&lt;/p&gt;

&lt;p&gt;So next time, you try to make it better — adding layers, abstractions, interfaces, dependency injection, and tests. The code now feels future-proof… until no one can understand it, or Product comes up with a new feature that makes the whole structure pointless.&lt;/p&gt;

&lt;p&gt;There’s a moment in every developer’s career when you realize that both too little and too much engineering hurt a project just the same — only in different ways. Welcome to the never-ending dance between underengineering and overengineering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don’t try to foresee too much
&lt;/h2&gt;

&lt;p&gt;We all want a future-proof codebase, but in reality every codebase has an expiration date. iOS is a great example: when Apple introduced Swift, many of us were excited—and also asked, why? We already had Xcode, Objective-C, simulators, Instruments—why did we need anything else? Suddenly there was a new language, and we faced the prospect of fewer and fewer developers comfortable with Objective-C while sitting on a huge Objective-C codebase. &lt;/p&gt;

&lt;p&gt;Requirements change, goals change, and standards evolve, so pieces inevitably get rewritten. Large platforms show this clearly: the Linux kernel, for instance, has replaced or refactored much of its early code over the years as it has grown. The lesson: build for the near-term horizon, and be ready to evolve.&lt;/p&gt;

&lt;h2&gt;
  
  
  But don’t block yourself
&lt;/h2&gt;

&lt;p&gt;Like in chess, good coding requires strategy. At first glance, “too complex” and “too simple” code seem like opposites, but if we accept change as inevitable, what really matters isn’t complexity — it’s adaptability. Adding a new feature should require as few modifications as possible and shouldn’t break existing contracts. And if we do make a mistake and need to rewrite a component, it’s far better when the code is simple, with minimal dependencies and layers.&lt;/p&gt;

&lt;p&gt;That’s why it’s usually better to start simple, but with room to change and evolve. For example, if we need to add a flag to our API that identifies a user as a moderator, it often makes more sense to introduce a more flexible parameter like &lt;code&gt;roles&lt;/code&gt; (a collection) instead of a plain boolean &lt;code&gt;is_moderator&lt;/code&gt;. Then, if we need to support additional roles in the future, we can simply add new ones instead of cluttering the API with more boolean flags.&lt;/p&gt;

&lt;p&gt;If we design our code to be more extensible, we might only need to write a small “plugin” or a new class that reuses existing components, without worrying that the entire codebase becomes more complex. It’s already complex because we’ve &lt;em&gt;pulled that complexity forward&lt;/em&gt;—we’ve extracted it from the future code we’re now implementing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Think about contracts
&lt;/h2&gt;

&lt;p&gt;One of the best ways to make a system more maintainable is to think in terms of APIs and contracts.&lt;br&gt;
When you start working on a complex feature, the first question should be: &lt;em&gt;how will this code be used in the existing codebase?&lt;/em&gt; That mindset can simplify a lot from the very beginning.&lt;/p&gt;

&lt;p&gt;I remember when I was a junior full-stack developer (Java + JavaScript) working in a team of more experienced engineers. I was tasked with building a &lt;strong&gt;date-range component&lt;/strong&gt; for a banking application. I assumed I could reuse as much code as possible, so I looked at the existing date picker and created an abstraction that could handle a date range instead of a single date.&lt;/p&gt;

&lt;p&gt;The result was not great:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The implementation was scattered across multiple files instead of being self-contained.&lt;/li&gt;
&lt;li&gt;The new component was complex to use — we had to create two independent date fields and then manually bind them through my abstraction layer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Apart from that, integrating my code into the dashboard turned out to be a nightmare:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"dateButtons"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;buttons-radio&lt;/span&gt;
        &lt;span class="na"&gt;model=&lt;/span&gt;&lt;span class="s"&gt;"dateRange"&lt;/span&gt;
        &lt;span class="na"&gt;options=&lt;/span&gt;&lt;span class="s"&gt;"dateRanges"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; 
        &lt;span class="na"&gt;ng-class=&lt;/span&gt;&lt;span class="s"&gt;'{active: highlighted}'&lt;/span&gt;
        &lt;span class="na"&gt;date-range-picker&lt;/span&gt; 
        &lt;span class="na"&gt;highlighted=&lt;/span&gt;&lt;span class="s"&gt;'customDateHighlighted'&lt;/span&gt;
        &lt;span class="na"&gt;date-range=&lt;/span&gt;&lt;span class="s"&gt;"customDateRange"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One of my senior colleagues suggested a complete rewrite and came up with this simple Angular API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;date-range-selector=&lt;/span&gt;&lt;span class="s"&gt;"dateRange"&lt;/span&gt; &lt;span class="na"&gt;date-ranges=&lt;/span&gt;&lt;span class="s"&gt;"dateRanges"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I was surprised that the internal implementation didn’t reuse the existing date field at all — it was actually &lt;strong&gt;much simpler&lt;/strong&gt; than mine.&lt;br&gt;
That experience taught me that reuse isn’t always the best choice, but &lt;strong&gt;thinking about how others will use your code always is&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Be pragmatic
&lt;/h2&gt;

&lt;p&gt;Once, I was involved in developing an iOS banking application. We had a really nice API for creating forms, mainly used for transfers. The API was well thought out and required very little effort to build a new form. I don’t remember the exact code, but it looked something like this (though at that time it was written in Objective-C):&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;var&lt;/span&gt; &lt;span class="nv"&gt;internalTransferForm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;FormBuilder&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;firstNameField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;FormField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"first_name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&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;lastNameField&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;FormField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"last_name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&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;ibanField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;FormField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"iban"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iban&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;internalTransferForm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addFields&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;firstNameField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lastNameField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ibanField&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addSubview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;internalTransferForm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;It worked almost magically, handling things like signing, validation, and submission under the hood.&lt;br&gt;
Or at least — it did, until we had to add a field that wasn’t sent to the backend or signed at all, but still needed to behave like a regular form field on the client side.&lt;/p&gt;

&lt;p&gt;We struggled for a few days with that new requirement, considering whether to add a boolean flag to our &lt;code&gt;FormField&lt;/code&gt; definition that would mark fields as &lt;code&gt;nonSendable&lt;/code&gt; or &lt;code&gt;clientOnly&lt;/code&gt;, and then extend our coordinator to handle that new parameter. But that approach would have required changes in many places across the codebase.&lt;/p&gt;

&lt;p&gt;Then one of the junior developers suggested that we could simply use the &lt;code&gt;UITableView&lt;/code&gt; API directly and add the field there — after all, our &lt;code&gt;Form&lt;/code&gt; was essentially a &lt;code&gt;UITableView&lt;/code&gt; under the hood. We gave it a try, and it worked perfectly.&lt;/p&gt;

&lt;p&gt;In the end, we never needed another field like that again, so extending our entire &lt;code&gt;FormField&lt;/code&gt; abstraction for this single case would have been a poor decision. Sometimes, the pragmatic shortcut really is the right one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep it DRY, but not &lt;em&gt;super&lt;/em&gt; DRY
&lt;/h2&gt;

&lt;p&gt;“Don’t repeat yourself” is a wonderful principle. In a perfect world, we’d anticipate every future use case, and our code would be perfectly composed into small, reusable components — with no duplication at all.&lt;/p&gt;

&lt;p&gt;I’ve seen too many codebases that ignore this rule, and the result is always the same: a nightmare to change anything. When we try to add new logic in one place, we suddenly have to update it everywhere.&lt;/p&gt;

&lt;p&gt;The real problem appears when we notice that we need to add that new logic in places A, B, and C — but not in D. That’s when we realize something isn’t quite right.&lt;/p&gt;

&lt;p&gt;But we can also easily go too far in the other direction. Imagine we have two entities — let’s say two DTOs — that looked identical at the time we implemented them, so we decided to extract their common fields into a parent class.&lt;/p&gt;

&lt;p&gt;A few weeks (or months) later, it turns out that the relationship between them was only &lt;em&gt;illusory&lt;/em&gt;. By then, we’ve written a lot of new code that depends on this false inheritance. Now we not only have to split these DTOs again, but also rewrite much of the code that was built on top of that mistaken relationship.&lt;/p&gt;

&lt;p&gt;Recently, I discovered a similar issue in the codebase I work on. A struct named &lt;code&gt;ApiError&lt;/code&gt;, conforming to &lt;code&gt;Error&lt;/code&gt;, was actually a REST API response type that also conformed to &lt;code&gt;Decodable&lt;/code&gt;. It was being reused as an error type. Inside that struct, you could find both fields coming from the backend and additional fields used for internal error handling. As more and more cases had to be considered, we kept adding new fields to something that was supposed to be just a simple DTO.&lt;/p&gt;

&lt;p&gt;Fortunately, we caught it early — before too much dependent code was written outside of our networking layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thought
&lt;/h2&gt;

&lt;p&gt;Finding the balance between the simplest implementation and something overly engineered can be challenging. We all aim to build future-proof codebases, but it’s hard to truly foresee the future. What we can always do, though, is ask ourselves a few simple questions: Can I explain this code to someone in five minutes? Is the API easy to extend? How easily could this code adapt to a constantly evolving environment?&lt;/p&gt;

&lt;p&gt;We should never feel unhappy about what we push to our repositories. If something requires cutting corners due to time constraints, we should at least ensure that what we add is easy to reverse — something we can later replace with a more durable implementation.&lt;/p&gt;

&lt;p&gt;There’s nothing more persistent than a temporary solution but if we stay aware of it, document it, and design for change, it doesn’t have to haunt us forever. Good engineering isn’t about avoiding compromises. It’s about making them consciously.&lt;/p&gt;




&lt;p&gt;If you enjoyed this post, you can find more of my writing on my blog:&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://michalcichon.github.io/blog/" rel="noopener noreferrer"&gt;https://michalcichon.github.io/blog/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>softwareengineering</category>
      <category>cleancode</category>
      <category>iosdev</category>
    </item>
    <item>
      <title>From Crypto Mining to Folding@Home: Using GPU Power for Something Good</title>
      <dc:creator>Michał Cichoń</dc:creator>
      <pubDate>Wed, 08 Oct 2025 14:52:09 +0000</pubDate>
      <link>https://dev.to/michal_cichon/from-crypto-mining-to-foldinghome-using-gpu-power-for-something-good-3321</link>
      <guid>https://dev.to/michal_cichon/from-crypto-mining-to-foldinghome-using-gpu-power-for-something-good-3321</guid>
      <description>&lt;p&gt;We often talk about how powerful our GPUs are — how many teraflops, how many frames per second. But have you ever wondered what else they could do besides mining crypto or running LLMs?&lt;/p&gt;

&lt;p&gt;A few weeks ago, I decided to explore a different path — using that same computing power for something that might actually help science. This led me to Folding@Home, a distributed research project that turns idle PCs into part of one of the world’s largest supercomputers.&lt;/p&gt;

&lt;p&gt;Here’s what I learned about ethical computing, personal responsibility, and the surprising impact of donating a few spare GPU cycles.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Read the full story on my blog&lt;/strong&gt;: &lt;a href="http://michalcichon.github.io/technology/2025/10/08/ethical-computing-cryptocurrencies-ai-protein-folding-at-home.html" rel="noopener noreferrer"&gt;http://michalcichon.github.io/technology/2025/10/08/ethical-computing-cryptocurrencies-ai-protein-folding-at-home.html&lt;/a&gt;&lt;/p&gt;

</description>
      <category>computing</category>
      <category>ethics</category>
      <category>ai</category>
      <category>foldingathome</category>
    </item>
    <item>
      <title>🤖 Vibe-coding a simple game with ChatGPT, Claude, and Gemini — who did it best?</title>
      <dc:creator>Michał Cichoń</dc:creator>
      <pubDate>Thu, 02 Oct 2025 18:19:41 +0000</pubDate>
      <link>https://dev.to/michal_cichon/vibe-coding-a-simple-game-with-chatgpt-claude-and-gemini-who-did-it-best-1j2d</link>
      <guid>https://dev.to/michal_cichon/vibe-coding-a-simple-game-with-chatgpt-claude-and-gemini-who-did-it-best-1j2d</guid>
      <description>&lt;p&gt;Take a look at my new article — this time, I’m vibe-coding a simple game with ChatGPT, Claude, and Gemini.&lt;/p&gt;

&lt;p&gt;The idea was simple: I wanted to see how far I could go without writing a single line of code myself. I described what I wanted, pointed out the issues, and let the models do the heavy lifting.  &lt;/p&gt;

&lt;p&gt;The results? Some hilarious failures, some surprising wins — and a lot of insight into how these models &lt;em&gt;actually think&lt;/em&gt; when tasked with a small but complete project.&lt;/p&gt;

&lt;p&gt;💡 In the post, you’ll see:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How each model approached the same problem
&lt;/li&gt;
&lt;li&gt;What kind of mistakes they made
&lt;/li&gt;
&lt;li&gt;How close they got to a working game
&lt;/li&gt;
&lt;li&gt;Screenshots and examples of the generated code
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 &lt;strong&gt;Read the full article here:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://michalcichon.github.io/programming/2025/10/01/game-vibecoded-in-chatgpt-gemini-and-claude.html" rel="noopener noreferrer"&gt;https://michalcichon.github.io/programming/2025/10/01/game-vibecoded-in-chatgpt-gemini-and-claude.html&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>chatgpt</category>
      <category>gemini</category>
      <category>programming</category>
    </item>
    <item>
      <title>Take a look at my new article — this time, I’m vibe-coding a simple game with ChatGPT, Claude, and Gemini.

https://michalcichon.github.io/programming/2025/10/01/game-vibecoded-in-chatgpt-gemini-and-claude.html</title>
      <dc:creator>Michał Cichoń</dc:creator>
      <pubDate>Thu, 02 Oct 2025 18:11:06 +0000</pubDate>
      <link>https://dev.to/michal_cichon/take-a-look-at-my-new-article-this-time-im-vibe-coding-a-simple-game-with-chatgpt-claude-and-kk4</link>
      <guid>https://dev.to/michal_cichon/take-a-look-at-my-new-article-this-time-im-vibe-coding-a-simple-game-with-chatgpt-claude-and-kk4</guid>
      <description>&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://michalcichon.github.io/programming/2025/10/01/game-vibecoded-in-chatgpt-gemini-and-claude.html" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmichalcichon.github.io%2Fassets%2F2025-10-01%2Fthumbnail.png" height="533" class="m-0" width="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://michalcichon.github.io/programming/2025/10/01/game-vibecoded-in-chatgpt-gemini-and-claude.html" rel="noopener noreferrer" class="c-link"&gt;
            How I Vibe-Coded a Game Using ChatGPT, Claude and Gemini | Michał Cichoń · iOS Software Engineer
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Coding without actually writing any code sounds tempting. In this article, I tested the capabilities of three popular LLMs: ChatGPT 5, Claude (Sonnet 4.5), and Gemini (2.5 Pro).
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmichalcichon.github.io%2Ficon.png" width="64" height="64"&gt;
          michalcichon.github.io
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
    </item>
  </channel>
</rss>
