<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Krzysztof Zabłocki]]></title><description><![CDATA[Creating award-winning apps, building tools and frameworks used by 80,000+ teams including Apple, Airbnb, Disney.]]></description><link>https://www.merowing.info/</link><image><url>https://www.merowing.info/favicon.png</url><title>Krzysztof Zabłocki</title><link>https://www.merowing.info/</link></image><generator>Ghost 5.79</generator><lastBuildDate>Tue, 20 Feb 2024 09:10:05 GMT</lastBuildDate><atom:link href="https://www.merowing.info/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Introducing Swifty Stack]]></title><description><![CDATA[What if I told you that you could save hours of development time every week? Swifty Stack course will help you master the most efficient frameworks, tools, and techniques to accelerate your app creations like never before.
]]></description><link>https://www.merowing.info/swifty-stack/</link><guid isPermaLink="false">65421243adad5600015ca557</guid><dc:creator><![CDATA[Krzysztof Zabłocki]]></dc:creator><pubDate>Wed, 01 Nov 2023 14:00:23 GMT</pubDate><media:content url="https://www.merowing.info/content/images/2023/11/CleanShot-2023-11-01-at-10.12.10@2x.png" medium="image"/><content:encoded><![CDATA[<h3 id="how-much-is-your-time-worth"><strong>How much is your time worth?</strong></h3><img src="https://www.merowing.info/content/images/2023/11/CleanShot-2023-11-01-at-10.12.10@2x.png" alt="Introducing Swifty Stack"><p>This is a question I&apos;ve asked myself when I was younger and started to get into <a href="https://www.merowing.info/the-journey-to-financial-independence/">Financial Independence</a> literature. <br><br>I had one important realisation:</p><div class="kg-card kg-callout-card kg-callout-card-blue"><div class="kg-callout-emoji">&#x1F92F;</div><div class="kg-callout-text"><b><strong style="white-space: pre-wrap;">You can always make more money, but you can never pay money to recover the time you lost.</strong></b></div></div><p>In-fact engineers have very high earning potential, we can get to the level where we are able to charge <strong>more than 100$ per hour</strong>!</p><p>I spent the next decade working on tools and techniques that can save engineering time and ended up speaking at over <a href="https://merowing.info/speaking?ref=merowing.info">50 conferences</a> and <a href="https://github.com/krzysztofzablocki?ref=merowing.info">open sourcing over 20 projects</a> to solve this exact problem!</p><blockquote><strong>What if I told you that you could save hours of development time every week?</strong></blockquote><h2 id="introducing-swifty-stackvideo-course">Introducing Swifty Stack - Video Course</h2><p>Today I&apos;m launching a new Indie product, a video course that will help you master the most efficient frameworks, tools, and techniques to accelerate your app creations like never before.</p><p>I tried to make <strong>Swifty Stack</strong> as universal as possible, so that regardless of what kind of product you are working on, or what architecture you decided to use for it you could use tools, frameworks and techniques that I mention in this course.</p><p>I also made this course as succinct and focused as I could, giving you a high level overview of things that could improve different aspects of creating applications. <strong>All of the things that I&apos;m talking about in the course I&apos;ve used myself either in commercial projects for my clients or my indie apps.</strong></p><div class="kg-card kg-callout-card kg-callout-card-blue"><div class="kg-callout-emoji">&#x1F4B5;</div><div class="kg-callout-text">I&apos;m offering the course at a discount for launch week, the course also features exclusive discounts for tools and educational materials you can leverage for iOS/Mac development.<br><br><b><strong style="white-space: pre-wrap;">The discounts are so high that if you them fully the course end up being completely free.</strong></b></div></div><p>Head to the website to get the course now:</p><div class="kg-card kg-product-card">
            <div class="kg-product-card-container">
                <img src="https://www.merowing.info/content/images/2023/11/CleanShot-2023-10-26-at-15.28.23-1.png" width="1354" height="820" class="kg-product-card-image" loading="lazy" alt="Introducing Swifty Stack">
                <div class="kg-product-card-title-container">
                    <h4 class="kg-product-card-title"><span style="white-space: pre-wrap;">Swifty Stack</span></h4>
                </div>
                
                    <div class="kg-product-card-rating">
                        <span class="kg-product-card-rating-active kg-product-card-rating-star"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M12.729,1.2l3.346,6.629,6.44.638a.805.805,0,0,1,.5,1.374l-5.3,5.253,1.965,7.138a.813.813,0,0,1-1.151.935L12,19.934,5.48,23.163a.813.813,0,0,1-1.151-.935L6.294,15.09.99,9.837a.805.805,0,0,1,.5-1.374l6.44-.638L11.271,1.2A.819.819,0,0,1,12.729,1.2Z"/></svg></span>
                        <span class="kg-product-card-rating-active kg-product-card-rating-star"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M12.729,1.2l3.346,6.629,6.44.638a.805.805,0,0,1,.5,1.374l-5.3,5.253,1.965,7.138a.813.813,0,0,1-1.151.935L12,19.934,5.48,23.163a.813.813,0,0,1-1.151-.935L6.294,15.09.99,9.837a.805.805,0,0,1,.5-1.374l6.44-.638L11.271,1.2A.819.819,0,0,1,12.729,1.2Z"/></svg></span>
                        <span class="kg-product-card-rating-active kg-product-card-rating-star"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M12.729,1.2l3.346,6.629,6.44.638a.805.805,0,0,1,.5,1.374l-5.3,5.253,1.965,7.138a.813.813,0,0,1-1.151.935L12,19.934,5.48,23.163a.813.813,0,0,1-1.151-.935L6.294,15.09.99,9.837a.805.805,0,0,1,.5-1.374l6.44-.638L11.271,1.2A.819.819,0,0,1,12.729,1.2Z"/></svg></span>
                        <span class="kg-product-card-rating-active kg-product-card-rating-star"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M12.729,1.2l3.346,6.629,6.44.638a.805.805,0,0,1,.5,1.374l-5.3,5.253,1.965,7.138a.813.813,0,0,1-1.151.935L12,19.934,5.48,23.163a.813.813,0,0,1-1.151-.935L6.294,15.09.99,9.837a.805.805,0,0,1,.5-1.374l6.44-.638L11.271,1.2A.819.819,0,0,1,12.729,1.2Z"/></svg></span>
                        <span class="kg-product-card-rating-active kg-product-card-rating-star"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M12.729,1.2l3.346,6.629,6.44.638a.805.805,0,0,1,.5,1.374l-5.3,5.253,1.965,7.138a.813.813,0,0,1-1.151.935L12,19.934,5.48,23.163a.813.813,0,0,1-1.151-.935L6.294,15.09.99,9.837a.805.805,0,0,1,.5-1.374l6.44-.638L11.271,1.2A.819.819,0,0,1,12.729,1.2Z"/></svg></span>
                    </div>
                

                <div class="kg-product-card-description"><p><span style="white-space: pre-wrap;">Master the most efficient frameworks, tools, and techniques to accelerate your app creations like never before.</span></p></div>
                
                    <a href="https://www.swiftystack.com/?ref=merowing.info" class="kg-product-card-button kg-product-card-btn-accent" target="_blank" rel="noopener noreferrer"><span>Open Website</span></a>
                
            </div>
        </div>]]></content:encoded></item><item><title><![CDATA[Creating Developer Tool - Part 2]]></title><description><![CDATA[Continuing series on crafting your very own developer tool, we're delving into the heart of the process! Explore the intricacies of modeling log entries and building a dynamic filtering system that tailors to your needs.]]></description><link>https://www.merowing.info/creating-developer-tool-part-2/</link><guid isPermaLink="false">64d3763aabacd70001b0f9d6</guid><category><![CDATA[Architecture]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[Workflow]]></category><category><![CDATA[GitHub Project]]></category><dc:creator><![CDATA[Krzysztof Zabłocki]]></dc:creator><pubDate>Wed, 09 Aug 2023 12:22:43 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1586864387967-d02ef85d93e8?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDI2fHx0b29sfGVufDB8fHx8MTY5MTU4MzQ4OXww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1586864387967-d02ef85d93e8?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDI2fHx0b29sfGVufDB8fHx8MTY5MTU4MzQ4OXww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Creating Developer Tool - Part 2"><p></p>
<p>Building upon the foundation of the draggable overlay button detailed in the <a href="https://www.merowing.info/creating-dev-tool-part-1/">first part of this series</a>, we now move forward to constructing the actual developer tool. This next phase involves diving into the intricacies of the <code>SpyDashboardModel</code> core and designing core model objects like tags, categories, integrations, and entries.</p>
<figure class="kg-card kg-video-card kg-width-regular kg-card-hascaption" data-kg-thumbnail="https://www.merowing.info/content/media/2023/08/Dashboard_thumb.jpg" data-kg-custom-thumbnail>
            <div class="kg-video-container">
                <video src="https://www.merowing.info/content/media/2023/08/Dashboard.mp4" poster="https://img.spacergif.org/v1/1010x446/0a/spacer.png" width="1010" height="446" loop autoplay muted playsinline preload="metadata" style="background: transparent url(&apos;https://www.merowing.info/content/media/2023/08/Dashboard_thumb.jpg&apos;) 50% 50% / cover no-repeat;"></video>
                <div class="kg-video-overlay">
                    <button class="kg-video-large-play-icon">
                        <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                            <path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/>
                        </svg>
                    </button>
                </div>
                <div class="kg-video-player-container kg-video-hide">
                    <div class="kg-video-player">
                        <button class="kg-video-play-icon">
                            <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                                <path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/>
                            </svg>
                        </button>
                        <button class="kg-video-pause-icon kg-video-hide">
                            <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                                <rect x="3" y="1" width="7" height="22" rx="1.5" ry="1.5"/>
                                <rect x="14" y="1" width="7" height="22" rx="1.5" ry="1.5"/>
                            </svg>
                        </button>
                        <span class="kg-video-current-time">0:00</span>
                        <div class="kg-video-time">
                            /<span class="kg-video-duration">0:16</span>
                        </div>
                        <input type="range" class="kg-video-seek-slider" max="100" value="0">
                        <button class="kg-video-playback-rate">1&#xD7;</button>
                        <button class="kg-video-unmute-icon">
                            <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                                <path d="M15.189 2.021a9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h1.794a.249.249 0 0 1 .221.133 9.73 9.73 0 0 0 7.924 4.85h.06a1 1 0 0 0 1-1V3.02a1 1 0 0 0-1.06-.998Z"/>
                            </svg>
                        </button>
                        <button class="kg-video-mute-icon kg-video-hide">
                            <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                                <path d="M16.177 4.3a.248.248 0 0 0 .073-.176v-1.1a1 1 0 0 0-1.061-1 9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h.114a.251.251 0 0 0 .177-.073ZM23.707 1.706A1 1 0 0 0 22.293.292l-22 22a1 1 0 0 0 0 1.414l.009.009a1 1 0 0 0 1.405-.009l6.63-6.631A.251.251 0 0 1 8.515 17a.245.245 0 0 1 .177.075 10.081 10.081 0 0 0 6.5 2.92 1 1 0 0 0 1.061-1V9.266a.247.247 0 0 1 .073-.176Z"/>
                            </svg>
                        </button>
                        <input type="range" class="kg-video-volume-slider" max="100" value="100">
                    </div>
                </div>
            </div>
            <figcaption><p><span>SpyDashboard demo</span></p></figcaption>
        </figure>
]]></content:encoded></item><item><title><![CDATA[Creating a developer tool - part 1]]></title><description><![CDATA[Crafting a dev tool? Here's how I'm implementing an easy-to-use toggle to unveil a more complex UI for my latest project.]]></description><link>https://www.merowing.info/creating-dev-tool-part-1/</link><guid isPermaLink="false">64b5308905292700012cc1c8</guid><category><![CDATA[GitHub Project]]></category><category><![CDATA[Swift]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[Research & Development]]></category><dc:creator><![CDATA[Krzysztof Zabłocki]]></dc:creator><pubDate>Tue, 18 Jul 2023 12:00:39 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1503791228404-a79884146f98?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDE2fHxkZXZlbG9wZXIlMjB0b29sfGVufDB8fHx8MTY4OTY3NDAxM3ww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1503791228404-a79884146f98?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDE2fHxkZXZlbG9wZXIlMjB0b29sfGVufDB8fHx8MTY4OTY3NDAxM3ww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Creating a developer tool - part 1"><p>As Swift developers, we often require easily accessible developer tools. <br>Similar to my <a href="https://github.com/krzysztofzablocki/LifetimeTracker?ref=merowing.info">LifetimeTracker</a>, you might desire a draggable button that can be pinned to screen edges and is responsive to screen changes. <br><br>This series of articles aims to guide you through the process of creating a new dev tool and we&apos;ll start with just that: an overlay button that acts as an access point to a  tool that we will be developing in the subsequent articles. Our primary target platforms are iOS and macOS.</p>
<figure class="kg-card kg-video-card kg-width-regular" data-kg-thumbnail="https://www.merowing.info/content/media/2023/07/CleanShot-2023-07-18-at-11.48.27-1_thumb.jpg" data-kg-custom-thumbnail>
            <div class="kg-video-container">
                <video src="https://www.merowing.info/content/media/2023/07/CleanShot-2023-07-18-at-11.48.27-1.mp4" poster="https://img.spacergif.org/v1/790x630/0a/spacer.png" width="790" height="630" playsinline preload="metadata" style="background: transparent url(&apos;https://www.merowing.info/content/media/2023/07/CleanShot-2023-07-18-at-11.48.27-1_thumb.jpg&apos;) 50% 50% / cover no-repeat;"></video>
                <div class="kg-video-overlay">
                    <button class="kg-video-large-play-icon">
                        <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                            <path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/>
                        </svg>
                    </button>
                </div>
                <div class="kg-video-player-container">
                    <div class="kg-video-player">
                        <button class="kg-video-play-icon">
                            <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                                <path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/>
                            </svg>
                        </button>
                        <button class="kg-video-pause-icon kg-video-hide">
                            <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                                <rect x="3" y="1" width="7" height="22" rx="1.5" ry="1.5"/>
                                <rect x="14" y="1" width="7" height="22" rx="1.5" ry="1.5"/>
                            </svg>
                        </button>
                        <span class="kg-video-current-time">0:00</span>
                        <div class="kg-video-time">
                            /<span class="kg-video-duration">0:10</span>
                        </div>
                        <input type="range" class="kg-video-seek-slider" max="100" value="0">
                        <button class="kg-video-playback-rate">1&#xD7;</button>
                        <button class="kg-video-unmute-icon">
                            <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                                <path d="M15.189 2.021a9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h1.794a.249.249 0 0 1 .221.133 9.73 9.73 0 0 0 7.924 4.85h.06a1 1 0 0 0 1-1V3.02a1 1 0 0 0-1.06-.998Z"/>
                            </svg>
                        </button>
                        <button class="kg-video-mute-icon kg-video-hide">
                            <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                                <path d="M16.177 4.3a.248.248 0 0 0 .073-.176v-1.1a1 1 0 0 0-1.061-1 9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h.114a.251.251 0 0 0 .177-.073ZM23.707 1.706A1 1 0 0 0 22.293.292l-22 22a1 1 0 0 0 0 1.414l.009.009a1 1 0 0 0 1.405-.009l6.63-6.631A.251.251 0 0 1 8.515 17a.245.245 0 0 1 .177.075 10.081 10.081 0 0 0 6.5 2.92 1 1 0 0 0 1.061-1V9.266a.247.247 0 0 1 .073-.176Z"/>
                            </svg>
                        </button>
                        <input type="range" class="kg-video-volume-slider" max="100" value="100">
                    </div>
                </div>
            </div>
            
        </figure>
]]></content:encoded></item><item><title><![CDATA[The Journey to Financial Independence as a Programmer]]></title><description><![CDATA[Achieving Financial Independence and leveraging this to improve your lifestyle offering you more control and choice]]></description><link>https://www.merowing.info/the-journey-to-financial-independence/</link><guid isPermaLink="false">6492e207583ecf0001504d23</guid><category><![CDATA[Mindset]]></category><dc:creator><![CDATA[Krzysztof Zabłocki]]></dc:creator><pubDate>Wed, 21 Jun 2023 14:41:56 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1579621970563-ebec7560ff3e?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDIwfHxmaW5hbmNpYWwlMjBpbmRlcGVuZGVuY2V8ZW58MHx8fHwxNjg3MzU4NTU5fDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1579621970563-ebec7560ff3e?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDIwfHxmaW5hbmNpYWwlMjBpbmRlcGVuZGVuY2V8ZW58MHx8fHwxNjg3MzU4NTU5fDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="The Journey to Financial Independence as a Programmer"><p>In a world obsessed with hustling, the concept of Financial Independence and Retire Early (FIRE) is a refreshing perspective on life and finances. It&apos;s about taking control of your financial destiny and living a life fueled by passion and choice rather than obligation. This is my personal account of achieving financial independence as a programmer and how you can do it too. </p>
<h2 id="what-is-financial-independence">What is Financial Independence?</h2>
<p>Financial independence is the status of having enough income to pay one&apos;s living expenses for the rest of one&apos;s life without having to be employed or dependent on others. This idea of becoming financially self-sufficient is appealing to many, but how can it be achieved? There are different paths to financial independence and your strategy may depend on your lifestyle preferences, income level, and savings rate.</p>
<p>Within the FIRE movement, there are two primary paths: Fat FIRE and Lean FIRE.</p>
<p>Fat FIRE implies aiming for a more traditional lifestyle, generally necessitating a high salary and aggressive savings and investment strategies. It&#x2019;s for those who don&#x2019;t want to reduce their current standard of living but still want to retire early.</p>
<p>On the other hand, Lean FIRE requires minimalist living and extreme savings, living on $25,000 or less per year. This may involve a frugal lifestyle, but it also promises an earlier exit from the workforce.</p>
<h2 id="what-is-your-target-fi-number">What is your target FI number?</h2>
<p>The basic principle of calculating your FI number revolves around the &apos;25 times rule&apos; and the &apos;4% rule&apos;. The &apos;25 times rule&apos; suggests that you need to have saved at least 25 times your annual expenses to retire. The &apos;4% rule&apos; states that you can withdraw 4% of your portfolio each year in retirement without exhausting your funds.</p>
<p>There are online calculators like <a href="https://www.firecalc.com/?ref=merowing.info">FireCalc</a> that can help you determine your unique FI number, taking into account your current savings, annual spending, and other important factors.</p>
<h2 id="my-journey-towards-financial-independence">My Journey Towards Financial Independence</h2>
<p>I began my journey towards financial independence at an early age. I started coding for cash before I even entered university. Although my earnings were modest, I adopted a frugal lifestyle to save and invest every penny I earned.</p>
<p>In the subsequent years, I juggled a full-time job, consulting, and even blogging. My work hours soared to 60-80 hours a week. Despite the grueling work schedule, I remained steadfast in my pursuit of financial independence.</p>
<p>When I started earning more, I resisted the urge to inflate my lifestyle. I maintained my frugal lifestyle and limited my spending to just 8-20% of my income, saving and investing the rest. I kept my focus on experiences over material possessions, avoiding unnecessary consumerism.</p>
<p>Along the way, I made savvy investment choices. I invested in rental properties, always ensuring that I negotiated hard and only bought properties that were a real steal. By the time I was 29, I had achieved lean financial independence.</p>
<h2 id="beyond-lean-fire-preparing-for-a-family">Beyond Lean FIRE: Preparing for a Family</h2>
<p>After achieving Lean FIRE, I realized that my financial goals needed to evolve as I was preparing for a new chapter in my life &#x2013; starting a family. This realization required me to shift my strategy from Lean FIRE to accumulating more wealth to secure my future family&apos;s financial stability.<br><br>Despite this shift, the freedom that comes with financial independence remains. The luxury of choice is still mine. I can still afford to say &apos;no&apos; to work engagements that don&apos;t excite me and even take on more risks knowing that my basic needs are already met.</p>
<h2 id="reach-fi-and-then-what">Reach FI and then what?</h2>
<p>Achieving financial independence doesn&apos;t mean you have to stop working. In fact, most people in the FIRE community continue to work in some capacity because they enjoy it, not because they have to.</p>
<p>Personally, I love programming and creating. It&apos;s been a part of my life since I was 8 years old, and I can&apos;t imagine giving it up. But, having achieved financial independence, I now have the freedom to pick and choose the projects that I want to work on. I no longer have to say &apos;yes&apos; to every client or job offer. I can prioritize projects that excite me and align with my personal goals.</p>
<h2 id="leverage-your-privilege-to-secure-your-future">Leverage your privilege to secure your future</h2>
<p>As programmers, we are often in a unique (and privileged) position. Our skills are in demand and we tend to earn relatively high salaries early in our careers. This offers an incredible opportunity to accelerate our journey towards financial independence.</p>
<p>The key is to start early, live frugally, and invest wisely. Choose a path - Lean FIRE or Fat FIRE - that aligns with your lifestyle and goals. Learn to strike a balance between the pursuit of wealth and the joy of living.</p>
<p>Remember, financial independence is not just about amassing wealth. It&apos;s about understanding the value of your time and energy. It&apos;s about gaining the freedom to choose - to say no to anything that doesn&apos;t add value to your life.</p>
<p><strong><em>Building financial independence as a programmer is a journey. But it&apos;s a journey well worth taking.</em></strong></p>
<div style="text-align:center"><a href="https://twitter.com/merowing_?ref_src=twsrc%5Etfw&amp;ref=merowing.info" class="twitter-follow-button" data-size="large" data-show-count="true">Follow @merowing_</a><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<h2 id="resources">Resources</h2>
<p>Here are some books and blogs that have guided me on my journey towards financial independence:</p>
<p>Books:</p>
<ol><li><a href="https://www.amazon.com/Your-Money-Life-Transforming-Relationship/dp/0143115766?ref=merowing.info">Your Money Or Your Life</a></li><li><a href="https://www.amazon.com/Rich-Dad-Poor-Teach-Middle/dp/1612680194?ref=merowing.info">Rich Dad, Poor Dad</a></li><li><a href="https://www.amazon.com/Die-Zero-Getting-Your-Money/dp/0358567092?keywords=die+with+zero&amp;qid=1687348462&amp;sr=8-1&amp;ref=merowing.info">Die With Zero</a></li></ol>
<p>Blogs:</p>
<ol><li><a href="https://www.mrmoneymustache.com/?ref=merowing.info">Mr. Money Mustache</a></li><li><a href="https://www.madfientist.com/?ref=merowing.info">Mad Fientist</a></li></ol>]]></content:encoded></item><item><title><![CDATA[Work-Life Balance for Sustainable Success]]></title><description><![CDATA[Caught in the relentless grind of the tech industry? Discover a better way. Dive into the crucial shift from overwork to balance and how it can transform your life and productivity. It's time we redefined success and debunked the myths of the 'Hustle Culture'.]]></description><link>https://www.merowing.info/work-life-balance-for-sustainable-success/</link><guid isPermaLink="false">64661d79346f3300015cd75b</guid><category><![CDATA[Mindset]]></category><dc:creator><![CDATA[Krzysztof Zabłocki]]></dc:creator><pubDate>Thu, 18 May 2023 13:01:25 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1482440308425-276ad0f28b19?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDN8fGh1c3RsZXxlbnwwfHx8fDE2ODQ0MTQ4NzF8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1482440308425-276ad0f28b19?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDN8fGh1c3RsZXxlbnwwfHx8fDE2ODQ0MTQ4NzF8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Work-Life Balance for Sustainable Success"><p>For over a decade, I&apos;ve lived a double life - an open-source advocate, innovator, and full-time professional. As an active member of the tech community, these dual roles are quite common. Inevitably, questions like &quot;How do you find the time?&quot; or &quot;Do you even sleep?&quot; arise. For 13 years, I found myself deeply entrenched in &quot;Hustle Culture,&quot; clocking 60-80h work weeks. But it took a year of peak earnings and a shortage of joyful memories to make me question this lifestyle: I was working hard, but was I truly living?</p><h2 id="the-lure-of-overwork-in-the-tech-industry">The Lure of Overwork in the Tech Industry</h2><p>In the tech world, overwork is often presented as a rite of passage. The promise of success is dangled like a carrot, implying a direct correlation between hours worked and rewards gained. This approach not only leads to burnout but also robs us of valuable life experiences. Our lives start to resemble an unending to-do list, as we continuously chase the next project or the next innovation.</p><p>This concept is thoroughly dissected in Alex Soojung-Kim Pang&apos;s <a href="https://www.amazon.com/Rest-More-Done-When-Work-ebook/dp/B01I64JYUS?ref=merowing.info">&quot;Rest: Why You Get More Done When You Work Less&quot;</a>, where he illustrates how the most successful people, many from the tech industry, leverage rest as a strategy for accomplishment.</p><h2 id="a-paradigm-shift-for-tech-professionals">A Paradigm Shift for Tech Professionals</h2><p>There is an alternative to this relentless grind. I made the conscious decision to shift gears. I began working less and focusing more on the essential tasks. This approach, often referred to as lifestyle design, prioritizes personal well-being over mindless hustle. After all, what use is success if we&apos;re too exhausted to enjoy it?</p><p><a href="https://www.amazon.com/Essentialism-Disciplined-Pursuit-Greg-McKeown-ebook/dp/B00HELB6XI?ref=merowing.info">&quot;Essentialism: The Disciplined Pursuit of Less&quot;</a> by Greg McKeown is a worthwhile read for anyone in tech. It hammers home the importance of doing less but doing it better, providing invaluable insights for anyone feeling overwhelmed by their workload.</p><h2 id="the-value-of-rest-and-leisure-for-creatives">The Value of Rest and Leisure for Creatives</h2><p>In our pursuit of the next big thing, it&apos;s easy to overlook the value of rest and leisure. These elements are not only essential for recharging our batteries but also foster creativity and productivity. Striking a balance between work, rest, and play isn&apos;t laziness&#x2014;it&apos;s a proactive approach to creating a fulfilling life.</p><p>In <a href="https://amzn.to/3ImtqUY?ref=merowing.info">&quot;Play: How it Shapes the Brain, Opens the Imagination, and Invigorates the Soul,&quot;</a> Dr. Stuart Brown explores the connection between play and creativity, shedding light on why many successful tech companies promote a playful work environment.</p><h2 id="the-result">The Result</h2><p>The result of this shift? Improved life satisfaction, a thriving spirit, and a more efficient work rhythm that doesn&apos;t rely on relentless grind.</p><h2 id="the-law-of-diminishing-returns-and-productivity">The Law of Diminishing Returns and Productivity</h2><p>A key principle underlying this shift is the <a href="https://en.wikipedia.org/wiki/Diminishing_returns?ref=merowing.info">&quot;Law of Diminishing Returns.&quot;</a> In the tech industry, we often push ourselves to the limit, working extra hours to meet deadlines or perfect projects. But beyond a certain point, working more doesn&apos;t equate to better results. It leads to stress, fatigue, and reduced productivity, as explored in <a href="https://www.amazon.com/Scarcity-having-little-means-much-ebook/dp/B00CUTLMKG?ref=merowing.info">&quot;Scarcity: Why Having Too Little Means So Much&quot;</a> by Sendhil Mullainathan and Eldar Shafir.</p><p>However, it&apos;s important to remember that this isn&apos;t solely an individual problem - it extends to how we manage teams in the tech industry. When managers push their teams towards overwork, it&apos;s often a shortsighted endeavor. While it might lead to short-term gains, in the long run, it costs more. Team members burn out and the recovery process can be lengthy. Ultimately, this leads to decreased productivity, morale, and even turnover, which all negatively affect a company&apos;s bottom line.</p><h2 id="challenging-the-hustle-culture-in-tech">Challenging the Hustle Culture in Tech</h2><p>So, it&apos;s time we challenged the status quo. Overwork isn&apos;t a badge of honor; it&apos;s a health hazard. Let&apos;s question the hustle culture endemic in our industry. The next time you&apos;re asked, &quot;When do you find the time?&quot; or &quot;Do you ever sleep?&quot; consider responding with, &quot;I make time, and yes, I prioritize sleep.&quot; Because you are not a machine, and your worth isn&apos;t defined by your productivity.</p><h2 id="a-call-to-balance-for-tech-professionals">A Call to Balance for Tech Professionals</h2><p>Let&apos;s champion balance, rest, and intentional living in the tech industry. We are human beings, not productivity machines. I&apos;d love to hear your thoughts on this, especially if you&apos;ve had similar experiences. Let&apos;s change the narrative together.</p><p><strong><em>Remember, effective work in tech isn&apos;t about working harder, but smarter.</em></strong></p><!--kg-card-begin: html--><div style="text-align:center"><a href="https://twitter.com/merowing_?ref_src=twsrc%5Etfw&amp;ref=merowing.info" class="twitter-follow-button" data-size="large" data-show-count="true">Follow @merowing_</a><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div><!--kg-card-end: html--><hr><p><strong>PS.</strong> To all my fellow engineers working on Apple platforms! I&apos;ve spent over a decade crafting tools and techniques to boost developer efficiency. Used by more than 80,000 teams worldwide&#x2014;including at <strong>Apple</strong>, <strong>Disney</strong>, and <strong>Airbnb</strong>&#x2014;these resources can revolutionize your workflow. Why work harder when you can work smarter? Bring more balance to your life and make your off-time more enjoyable. Check out <a href="https://www.merowing.info/">my tools</a> or <a href="https://merowing.info/consulting?ref=merowing.info">hire me</a> and let&apos;s redefine the future of work together.</p>]]></content:encoded></item><item><title><![CDATA[Optimizing Swift: Tracking property changes and building a Memoization system]]></title><description><![CDATA[Tracking property changes and intelligently avoids redundant computations by leveraging custom memoization system, leading to more efficient and performant apps.]]></description><link>https://www.merowing.info/memoization-part-1/</link><guid isPermaLink="false">642c7140abed5c003d5cac42</guid><category><![CDATA[Architecture]]></category><category><![CDATA[Research & Development]]></category><category><![CDATA[The Composable Architecture]]></category><dc:creator><![CDATA[Krzysztof Zabłocki]]></dc:creator><pubDate>Wed, 05 Apr 2023 10:38:07 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1533279443086-d1c19a186416?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fG1lbW9yeXxlbnwwfHx8fDE2ODA2MzEyOTQ&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1533279443086-d1c19a186416?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fG1lbW9yeXxlbnwwfHx8fDE2ODA2MzEyOTQ&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Optimizing Swift: Tracking property changes and building a Memoization system"><p>As a Swift consultant, I always look for ways to optimize clients&apos; code and improve efficiency. I recently built a prototype for <a href="https://thebrowser.company/?ref=merowing.info">The Browser Company</a> that tracks changes in Swift properties and implements a memoization system. </p><p>This system re-executes complex computation scopes only when necessary, avoiding redundant re-computations and returning cached values when no property changes have been made. </p><p>In this article, I&apos;ll walk you through the inner workings of this prototype and demonstrate how it can enhance your Swift coding experience.</p><p>Here&apos;s a final demo from this series where you can see that it will re-compute just the selected Scopes depending on which properties in State I mutate.</p><figure class="kg-card kg-video-card kg-card-hascaption"><div class="kg-video-container"><video src="https://www.merowing.info/content/media/2023/04/Kapture-2023-04-05-at-12.30.20.mp4" poster="https://img.spacergif.org/v1/2560x1256/0a/spacer.png" width="2560" height="1256" loop autoplay muted playsinline preload="metadata" style="background: transparent url(&apos;https://www.merowing.info/content/images/2023/04/media-thumbnail-ember3367.jpg&apos;) 50% 50% / cover no-repeat;"></video><div class="kg-video-overlay"><button class="kg-video-large-play-icon"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/></svg></button></div><div class="kg-video-player-container kg-video-hide"><div class="kg-video-player"><button class="kg-video-play-icon"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/></svg></button><button class="kg-video-pause-icon kg-video-hide"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><rect x="3" y="1" width="7" height="22" rx="1.5" ry="1.5"/><rect x="14" y="1" width="7" height="22" rx="1.5" ry="1.5"/></svg></button><span class="kg-video-current-time">0:00</span><div class="kg-video-time">/<span class="kg-video-duration"></span></div><input type="range" class="kg-video-seek-slider" max="100" value="0"><button class="kg-video-playback-rate">1&#xD7;</button><button class="kg-video-unmute-icon"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M15.189 2.021a9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h1.794a.249.249 0 0 1 .221.133 9.73 9.73 0 0 0 7.924 4.85h.06a1 1 0 0 0 1-1V3.02a1 1 0 0 0-1.06-.998Z"/></svg></button><button class="kg-video-mute-icon kg-video-hide"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M16.177 4.3a.248.248 0 0 0 .073-.176v-1.1a1 1 0 0 0-1.061-1 9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h.114a.251.251 0 0 0 .177-.073ZM23.707 1.706A1 1 0 0 0 22.293.292l-22 22a1 1 0 0 0 0 1.414l.009.009a1 1 0 0 0 1.405-.009l6.63-6.631A.251.251 0 0 1 8.515 17a.245.245 0 0 1 .177.075 10.081 10.081 0 0 0 6.5 2.92 1 1 0 0 0 1.061-1V9.266a.247.247 0 0 1 .073-.176Z"/></svg></button><input type="range" class="kg-video-volume-slider" max="100" value="100"></div></div></div><figcaption>Memoized scopes and automatic dependency tracking</figcaption></figure><p> </p>]]></content:encoded></item><item><title><![CDATA[How to avoid burnout as a software engineer?]]></title><description><![CDATA[As a software engineer, burnout can be common due to the demanding nature of the job. Prioritizing self-care, setting realistic goals, managing workload, taking breaks, practicing self-care, and setting boundaries can help prevent burnout and maintain a healthy work-life balance.]]></description><link>https://www.merowing.info/how-to-avoid-burnout-as-a-software-engineer/</link><guid isPermaLink="false">64071e776e5d2b003dc04597</guid><category><![CDATA[Mindset]]></category><dc:creator><![CDATA[Krzysztof Zabłocki]]></dc:creator><pubDate>Tue, 07 Mar 2023 13:01:10 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1516302752625-fcc3c50ae61f?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDh8fGJ1cm5vdXR8ZW58MHx8fHwxNjc4MTk0MDg5&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1516302752625-fcc3c50ae61f?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDh8fGJ1cm5vdXR8ZW58MHx8fHwxNjc4MTk0MDg5&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="How to avoid burnout as a software engineer?"><p>As a software engineer, burnout can be a shared experience due to the demanding nature of the job. I&apos;ve experienced burnout several times throughout my career, and I&apos;ve learned that it&apos;s crucial to prioritize self-care and find a healthy work-life balance.<br><br>Here are six strategies that have helped me avoid burnout and maintain a healthy work-life balance.</p><h2 id="realistic-goals">Realistic Goals</h2><p>Setting realistic goals is essential to avoid feeling overwhelmed and burning out.<br></p><p>Even if I&apos;m building a new personal application, I break it down into more minor features and more manageable tasks that could be finished within a set timeframe. This can help manage workload and avoid feeling overwhelmed by the work left to do.</p><p>For guidance on how to do this effectively, I recommend checking out <a href="https://amzn.to/3JfMvcl?ref=merowing.info">&quot;Getting Things Done: The Art of Stress-Free Productivity&quot; by David Allen</a> and <a href="https://amzn.to/3JfMvcl?ref=merowing.info"><a href="https://amzn.to/3IZbpfb?ref=merowing.info">&quot;Atomic Habits: An Easy &amp; Proven Way to Build Good Habits &amp; Break Bad Ones&quot; by James Clear.</a></a></p><h2 id="workload-management">Workload management</h2><p>Managing your workload is crucial to avoid burnout. Prioritize your tasks based on their urgency and importance. Use project management tools like Trello or Asana to help you manage your workload effectively.</p><p>When working on a complex project with several deadlines, I used project management tools to help me prioritize my tasks. This allowed me to break down the project into smaller tasks and track my progress, which helped me manage my workload effectively.</p><p>For further reading on this topic, I recommend checking out <a href="https://www.amazon.com/Deep-Work-Focused-Success-Distracted-ebook/dp/B013UWFM52?crid=2O138Z8CWVAAM&amp;keywords=deep+work&amp;qid=1678189238&amp;s=digital-text&amp;sprefix=deep+wo%2Cdigital-text%2C180&amp;sr=1-1&amp;ref=merowing.info">&quot;Deep Work: Rules for Focused Success in a Distracted World&quot; by Cal Newport</a> and <a href="https://www.amazon.com/Essentialism-Disciplined-Pursuit-Greg-McKeown-ebook/dp/B00HELB6XI?keywords=essentialism&amp;qid=1678189266&amp;s=digital-text&amp;sprefix=essen%2Cdigital-text%2C185&amp;sr=1-1&amp;ref=merowing.info">&quot;Essentialism: The Disciplined Pursuit of Less&quot; by Greg McKeown</a>.</p><h2 id="effective-work">Effective work</h2><p>In addition to managing your workload, it&apos;s important to work efficiently and prioritize your tasks effectively. One technique I&apos;ve found helpful is the philosophy of &quot;One Thing.&quot; This philosophy, popularized by Gary Keller and Jay Papasan in their book &quot;<a href="https://amzn.to/3kQ0In5?ref=merowing.info">The One Thing: The Surprisingly Simple Truth Behind Extraordinary Results</a>&quot; involves focusing on the most important task that will impact your goals most.</p><p>When you&apos;re feeling overwhelmed or burnt out, trying to tackle everything on your to-do list at once can be tempting. But you can achieve more excellent results in less time by prioritizing the most critical task and focusing all your energy on completing it.</p><p>For example, if you&apos;re working on a project with several deadlines, it&apos;s easy to get caught up in completing all tasks simultaneously. However, by focusing on the one most important task to complete the project, you can make significant progress and reduce your stress levels.</p><p>In addition to helping you work more efficiently, the &quot;One Thing&quot; philosophy can help you avoid burnout by helping you prioritize your tasks and avoid feeling overwhelmed. Focusing on the most important task allows you to progress on your goals while avoiding the stress of doing too much at once.</p><h2 id="breaks">Breaks</h2><p>Taking breaks is essential to recharge your batteries and avoid burnout. Step away from your computer and engage in activities that help you relax and recharge, like taking a walk, meditating, or listening to music. </p><p>I worked on <a href="https://www.google.com/search?q=headspace&amp;oq=headspace&amp;sourceid=chrome&amp;ie=UTF-8&amp;ref=merowing.info">Headspace</a>, but nowadays, I use <a href="https://www.calm.com/?ref=merowing.info">Calm</a> for my mindfulness routine.</p><p>For further reading on the benefits of taking breaks, I recommend checking out <a href="https://amzn.to/41KBKGl?ref=merowing.info">&quot;The Power of Full Engagement: Managing Energy, Not Time, is the Key to High Performance and Personal Renewal&quot; by Jim Loehr and Tony Schwartz</a> and <a href="https://www.amazon.com/Rest-More-Done-When-Work-ebook/dp/B01I64JYUS?crid=26E1N4PJJ32NY&amp;keywords=Rest%3A+Why+You+Get+More+Done+When+You+Work+Less&amp;qid=1678189401&amp;s=digital-text&amp;sprefix=the+power+of+full+engagement+managing+energy%2C+not+time%2C+is+the+key+to+high+performance+and+personal+renewal%2Cdigital-text%2C180&amp;sr=1-1&amp;ref=merowing.info">&quot;Rest: Why You Get More Done When You Work Less&quot; by Alex Soojung-Kim Pang.</a></p><p>One example is when I sometimes work on a project for 10+ hours without taking a break, even if I&apos;m in the flow at that moment. It has brutal repercussions the next day. I usually feel like a zombie. Instead, if I break my work into 60-90 min blocks with 10-15-minute breaks, I can keep myself productive the whole week.</p><h2 id="self-care">Self-Care</h2><p>Practicing self-care is crucial to avoid burnout. Take care of your physical and mental health by eating well, getting enough sleep, exercising regularly, and engaging in activities that bring you joy and relaxation.</p><p>When I wasn&apos;t taking care of myself as well as I could be and didn&apos;t eat well, I wasn&apos;t very productive. When I started doing <a href="https://www.instagram.com/p/Cl86T_NIU27/?ref=merowing.info">bodybuilding at a pro level</a>, although it&apos;s hard physically, it helped me feel more energized and effective, improving my work performance.</p><p>For further reading on this topic, I recommend checking out <a href="https://amzn.to/3L35isL?ref=merowing.info">&quot;The Happiness Advantage: How a Positive Brain Fuels Success in Work and Life&quot; by Shawn Achor</a> and <a href="https://www.amazon.com/Body-Keeps-Score-Transformation-Trauma-ebook/dp/B00IICN1F8?crid=23L465MMM0J4&amp;keywords=The+Body+Keeps+the+Score%3A+Brain%2C+Mind%2C+and+Body+in+the+Healing+of+Trauma&amp;qid=1678189639&amp;s=digital-text&amp;sprefix=the+body+keeps+the+score+brain%2C+mind%2C+and+body+in+the+healing+of+trauma%2Cdigital-text%2C193&amp;sr=1-1&amp;ref=merowing.info">&quot;The Body Keeps the Score: Brain, Mind, and Body in the Healing of Trauma&quot; by Bessel van der Kolk.</a></p><h2 id="boundaries">Boundaries</h2><p>Setting boundaries is crucial to maintain a healthy work-life balance. Be clear about your working hours and ensure you have time for activities outside of work.</p><p>Sometimes I see people put in overtime because of imposter syndrome. It&apos;s a familiar feeling among software engineers. Recognize that everyone experiences imposter syndrome at some point in their career. I often do, even though I&apos;ve achieved a lot in my career, and one would think it should be enough, but our mind likes to play tricks on our perception of ourselves.</p><p>Talk to your peers, mentors, or a therapist to help you develop strategies to overcome these feelings.</p><p><br>One example is when I used overwork as a badge of honor when I was younger, working 60-80 hours per week. I realized this was not a healthy way to live and that I needed to set boundaries. I learned to relax and enjoy the non-productive time, even though it took some time to stop hearing a voice in my head, &apos;you could be doing something more productive&apos; when I just wanted to play some games or chill with music.</p><p>Related book <a href="https://amzn.to/3IWQIjP?ref=merowing.info"><a href="https://www.amazon.com/Imposter-Syndrome-Remedy-sabotage-flourish-ebook/dp/B07CLBLKYB?qid=1678190307&amp;sr=8-1&amp;ref=merowing.info">&quot;<a href="https://www.amazon.com/Imposter-Syndrome-Remedy-sabotage-flourish-ebook/dp/B07CLBLKYB?qid=1678190307&amp;sr=8-1&amp;ref=merowing.info">The Imposter Syndrome Remedy: A 30-Day Action Plan to Stop Feeling Like a Fraud</a>&quot;</a> by Emee Vida Estacio</a></p><h2 id="summary">Summary</h2><p>In summary, burnout is a common experience for software engineers due to the demanding nature of the job. </p><p>Setting realistic goals, managing workload, taking breaks, practicing self-care, and setting boundaries are crucial to avoid burnout. These strategies help to maintain a healthy work-life balance, avoid feeling overwhelmed, and prevent burnout. </p><p>By prioritizing self-care and finding a healthy work-life balance, software engineers can continue to perform at a high level without sacrificing their mental and physical health.</p><!--kg-card-begin: html--><div style="text-align:center"><a href="https://twitter.com/merowing_?ref_src=twsrc%5Etfw&amp;ref=merowing.info" class="twitter-follow-button" data-size="large" data-show-count="true">Follow @merowing_</a><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div><!--kg-card-end: html-->]]></content:encoded></item><item><title><![CDATA[The Composable Architecture - Best Practices]]></title><description><![CDATA[Set of best practices for The Composable Architecture projects based on experiences of the team at The Browser Company.]]></description><link>https://www.merowing.info/the-composable-architecture-best-practices/</link><guid isPermaLink="false">63f5dc4de2b23a004d5f24c2</guid><category><![CDATA[The Composable Architecture]]></category><dc:creator><![CDATA[Krzysztof Zabłocki]]></dc:creator><pubDate>Wed, 22 Feb 2023 12:00:55 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1553511434-53cc82692085?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDN8fGNvbXBvc2l0aW9ufGVufDB8fHx8MTY3NzA3MzA1OA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1553511434-53cc82692085?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDN8fGNvbXBvc2l0aW9ufGVufDB8fHx8MTY3NzA3MzA1OA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="The Composable Architecture - Best Practices"><p>The team at <a href="https://thebrowser.company/?ref=merowing.info">The Browser Company</a> is a major adopter of <a href="https://github.com/pointfreeco/swift-composable-architecture?ref=merowing.info">The Composable Architecture</a> (TCA) framework. Based on our team&apos;s experiences and insights from the wider community, I&apos;ve developed a new set of best practices that can benefit your TCA projects. </p><p>Here are some of the key practices to consider using in your projects.</p><h2 id="reducers">Reducers</h2><ul><li>To adhere to the <a href="https://www.merowing.info/boundries-in-tca/">boundaries approach</a>, it&apos;s best to name your view actions based on &quot;what happened&quot; rather than their expected effect. For instance, you should use <code>.didTapIncrementButton</code> instead of <code>.incrementCount</code>. This approach enables you to add logic to the action while still keeping it true to its name. Additionally, this practice encourages keeping business logic in the reducer instead of letting it slip into the view layer.</li></ul><!--kg-card-begin: markdown--><ul>
<li>Try to avoid performing expensive operations in Reducers.
<ul>
<li>Reducers run on the main thread, and such operations can cause lag or even freeze the UI. Instead, consider leveraging <code>.task</code> / <code>EffectTask</code> and environment clients, which allow you to perform these operations off the main thread. By doing so, you can ensure that your app remains responsive and performant.</li>
</ul>
</li>
</ul>
<pre><code class="language-swift">return .task { 
  .reducer(.onResultsUpdated(await self.search(query: query)) 
}
</code></pre>
<!--kg-card-end: markdown--><ul><li>When nilling out state, it&apos;s important to unregister any long-running effects to prevent memory leaks and bugs. To do this, you can use the <code>.cancellable</code> method to cancel any ongoing effects before the state is deallocated. By doing so, you can ensure that your app remains memory-efficient and stable.</li><li>When using <code>concatenate</code> and <code>merge</code> methods, it&apos;s important to consider their differences. The <code>concatenate</code> method will wait for previous effects to complete before running the next ones, while the <code>merge</code> method runs effects in parallel. However, it&apos;s important to note that relying on <code>concatenate</code> can become problematic when using other actions that might introduce delays in the future, such as animations. This can also delay any following effects. Therefore, it&apos;s often better to use the merge method to ensure that effects run in parallel and prevent delays that could impact the user experience.</li></ul><!--kg-card-begin: markdown--><ul>
<li>Avoid high-frequency actions like Timers hitting your reducer to check something. Instead, perform the work in the tasks or env clients and only send back actions when actual work on State needs to be performed.
<ul>
<li>An example might be mouse move handlers. When we track mouse movement, we often do so, waiting for some condition to be true. Best to check that condition in the views code and then only pipe the edge condition/events into TCA.</li>
</ul>
</li>
</ul>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><ul>
<li><strong>Don&apos;t use actions for sharing logic.</strong> <strong>They are not methods.</strong>
<ul>
<li>Sending actions is not as lightweight as calling a method on a type.</li>
<li>Each (async) action causes rescoping and equality checks across our application</li>
<li>Only exception for now: <code>.delegate()</code> actions</li>
<li><strong>Instead, use mutating methods on State objects</strong>
<ul>
<li><em>Currently exploring the alternative of extending <code>ReducerProtocol</code> implementations with functions that take <code>inout</code> State variable. This allows accessing the <code>Reducer</code>s dependencies without passing them into helper methods.</em></li>
</ul>
</li>
</ul>
</li>
</ul>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><ul>
<li>When possible, it&apos;s best to use feature state instead of projected state (computed properties) in your app. It can help you avoid unnecessary computation and improve your app&apos;s performance.
<ul>
<li>For instance, in your AppReducer, it&apos;s better to use <code>localWindows</code> instead of the <code>windows</code> computed property, if available. By doing so, you can avoid computing the windows property every time it&apos;s accessed, reducing the load on the main thread and improving overall app performance.</li>
</ul>
</li>
</ul>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><ul>
<li>When using <code>onChange</code> reducers, it&apos;s important to be careful and mindful of where you add them in the reducer composition. This is because onChange reducers only work at a specific level of the reducer composition.
<ul>
<li>If you&apos;re experiencing issues with <code>onChange</code> reducers not working, it&apos;s likely that they are added at the wrong level of reducer. For example, you may be observing at the Feature level, but the mutation occurs at the App level. To resolve this issue, make sure that you add the onChange reducers at the appropriate level of reducer composition to ensure that they function as intended.</li>
</ul>
</li>
</ul>
<!--kg-card-end: markdown--><ul><li>When observing child feature actions, use delegate actions from our <a href="https://www.merowing.info/boundries-in-tca/">boundaries convention</a>.</li></ul><h2 id="state-modeling">State Modeling</h2><!--kg-card-begin: markdown--><ul>
<li>Be extra careful with code inside the <code>scope</code> functions, e.g., computing child state.
<ul>
<li>Those calls happen for every action sent to the system, so they must be fast.
<ul>
<li>Try to avoid calculations. Even O(n) complexity will cause issues in hot paths for large sidebars.</li>
</ul>
</li>
<li>Either pre-compute heavy data or make the view layer calculate it if needed</li>
<li>Be especially careful when using computed properties, as they can be expensive due to the same problems as normal scope. Other than being in the hot-path, they often might re-create objects each time they are called.</li>
</ul>
</li>
<li>Make state optional when possible and leverage <code>ifLet</code> and <code>optional</code> pullbacks to avoid performing unnecessary work.
<ul>
<li>E.g., The command bar was <strong>not optional,</strong> so its state/reducer and view layer side effects would run even when it wasn&apos;t visible.</li>
</ul>
</li>
<li>UI State doesn&#x2019;t always need to persist.
<ul>
<li>E.g., sidebar hover used to be a state property, but it only needs to live at View Layer.</li>
</ul>
</li>
<li>Be careful when updating persisted state objects. They might require migrations. Make sure to add tests to avoid user data loss.</li>
<li>Avoid referring to <code>userDefaults</code> in scoping function. They should use the current <code>State</code> and not refer to anything else.</li>
<li>Projected state initializers should be as fast as possible, usually just initializers without any computations.</li>
</ul>
<!--kg-card-end: markdown--><h2 id="testing">Testing</h2><!--kg-card-begin: markdown--><ul>
<li>Use <code>prepareDependencies</code> and always use <code>initialState</code> explicitly
<ul>
<li>That allows state initializers to leverage test <code>@Dependency</code> values e.g. UUID generator.</li>
</ul>
</li>
</ul>
<!--kg-card-end: markdown--><pre><code class="language-swift">// Don&apos;t
let windows = WindowsState.stub(windows: [.init(window)], sidebar: .stub(globalSidebarContainer: sidebar))
let store = TestStore(
  initialState: windows,
  prepareDependencies: {
    $0.uuid = .incrementing
  }
)

// Do
let store = TestStore(
  initialState: .stub(windows: ...),
  prepareDependencies: {
    $0.uuid = .incrementing
  }
)</code></pre><h2 id="dependencies">Dependencies</h2><!--kg-card-begin: markdown--><ul>
<li>Use structs for clients with mutable <code>var</code> instead of <code>protocols</code>
<ul>
<li>
<p>Protocol-oriented interfaces for clients are discouraged in our codebase</p>
<pre><code class="language-swift">// Don&apos;t
protocol AudioPlayer {
  func loop(_ url: URL) async throws
  func play(_ url: URL) async throws
  func setVolume(_ volume: Float) async
  func stop() async
}

// Do
struct AudioPlayerClient {
  var loop: (_ url: URL) async throws -&gt; Void
  var play: (_ url: URL) async throws -&gt; Void
  var setVolume: (_ volume: Float) async -&gt; Void
  var stop: () async -&gt; Void
}
</code></pre>
<p>This allows us to describe the bare minimum of the dependency in tests. For example, suppose that one user flow of the feature you are testing invokes the&#xA0;<code>play</code>&#xA0;endpoint, but you don&#x2019;t think any other endpoint will be called. Then you can write a test that overrides only one endpoint and uses the default <code>.failing</code> version for all the others. By doing so, you can ensure that your tests are focused and efficient, making it easier to identify and resolve any issues that arise.</p>
<pre><code class="language-swift">let model = withDependencies {
  $0.audioPlayer.play = { _ in await isPlaying.setValue(true) }
} operation: {
  FeatureModel()
}
</code></pre>
</li>
</ul>
</li>
</ul>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><ul>
<li>Use placeholders in <code>unimplemented</code> failing stubs where the endpoints returns a value.
<ul>
<li>
<p>This allows your test suite to continue running even if a failure occurs. If an issue is detected, it&apos;s reported via XCTFail along with the name of the endpoint that caused the failure. This is preferable to a fatalError, which can cause the test to crash and drop into the debugger, interrupting the rest of the test suite.</p>
<pre><code class="language-swift">// DON&apos;T
searchHistory: unimplemented(&quot;\(Self.self).searchHistory&quot;)

// DO
searchHistory: unimplemented(&quot;\(Self.self).searchHistory&quot;, placeholder: [])
</code></pre>
</li>
</ul>
</li>
</ul>
<!--kg-card-end: markdown--><h2 id="view-layer">View layer</h2><!--kg-card-begin: markdown--><ul>
<li>Use the new <code>observe:</code> <code>ViewState</code> initializer as it forces you to create <code>ViewState</code></li>
</ul>
<pre><code class="language-swift">// DON&apos;T
viewStore = .init(store.scope(state: \.overlayViewState))

// DO
viewStore = .init(store, observe: \.overlayViewState)
</code></pre>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><ul>
<li>No need to create a <code>ViewStore</code> if you only need to send one-off actions, e.g., <code>ViewStore(store.stateless).send(.action)</code>
<ul>
<li>Note that we use <code>.stateless</code> variant, which skips temp <code>ViewStore</code> <a href="https://github.com/thebrowsercompany/swift-composable-architecture/blob/8fbe8bd42cedf8db2e4040aa18765b3ad0bcb60d/Sources/ComposableArchitecture/ViewStore.swift?ref=merowing.info#L259-L281">from taking part in the update pipeline</a>.</li>
</ul>
</li>
</ul>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><ul>
<li>Always scope down your <code>ViewStore</code> scopes to the minimal amount your <code>View</code> needs by introducing local <code>ViewState</code> for it, as this avoids unnecessary diffing and view reloads
<ul>
<li>Either only add actively observing properties to ViewState or consider creating multiple different ViewStores for more complex use-cases</li>
</ul>
</li>
</ul>
<!--kg-card-end: markdown--><ul><li>Besides a view-lifecycle action (e.g. <code>viewDidAppear</code>, or even better, <a href="https://www.pointfree.co/episodes/ep199-async-composable-architecture-effect-lifetimes?ref=merowing.info#t415" rel="noopener noreferrer">bind it to the lifetime of the view via a task</a>), there should be no other action sent into the store on appear or load. Especially for tableview cells, this can lead to a <em>ton</em> of actions being sent into the store on appearance, e.g. <code>onHover(false)</code></li><li>Mind that subscribing to a <code>ViewStore</code>&#x2019;s publisher triggers synchronously. So when in AppKit, consider adding a <code>dropFirst</code> when subscribing to viewStore changes, in case it&apos;s only about reacting to changes.</li></ul><h2 id="bugs-workarounds">Bugs &amp; Workarounds</h2><ul><li>If you break auto-completion in <code>ReducerProtocol</code> bodies, the workaround is to add explicit type to your definitions, e.g., <code>Reduce&lt;State, Action&gt; {</code></li><li>If the same or compiler errors happen when using <code>WithViewStore</code> either add explicit type in the body, e.g., <code>WithViewStore(self.store) { (viewStore: ViewStoreOf&lt;Feature&gt; in)</code> or introduce <code>@ObservedObject var viewStore: ViewStoreOf&lt;Feature&gt;</code> instead of the <code>WithViewStore</code> wrapper.</li></ul><p></p><h1 id="conclusion">Conclusion</h1><p><a href="https://thebrowser.company/?ref=merowing.info">The Browser Company</a> is a major adopter of <a href="https://github.com/pointfreeco/swift-composable-architecture?ref=merowing.info">The Composable Architecture</a> (TCA) framework. We have shared best practices based on our team&apos;s experiences and insights from the wider community. </p><p>These practices are designed to help you optimize the performance and stability of your TCA projects and make them more maintainable in the long term. By adopting these practices, you can build high-quality TCA projects that meet your users&apos; needs and exceed their expectations.</p><p>References:</p><ul><li><a href="https://github.com/pointfreeco/swift-composable-architecture/discussions/1666?ref=merowing.info">Community Tips and Tricks</a></li><li><a href="https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/performance/?ref=merowing.info">Official TCA performance recommendations</a></li><li><a href="https://pointfreeco.github.io/swift-dependencies/main/documentation/dependencies/designingdependencies?ref=merowing.info" rel="noopener noreferrer">Additional reading on PointFree Dependencies system</a></li></ul><!--kg-card-begin: html--><div style="text-align:center"><a href="https://twitter.com/merowing_?ref_src=twsrc%5Etfw&amp;ref=merowing.info" class="twitter-follow-button" data-size="large" data-show-count="true">Follow @merowing_</a><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div><!--kg-card-end: html-->]]></content:encoded></item><item><title><![CDATA[Widget Architecture - Part 3]]></title><description><![CDATA[Let's use Sourcery to automate and customize our Widget architecture.]]></description><link>https://www.merowing.info/widget-architecture-part-3/</link><guid isPermaLink="false">63eca87717b36c003d21d551</guid><category><![CDATA[Architecture]]></category><dc:creator><![CDATA[Krzysztof Zabłocki]]></dc:creator><pubDate>Wed, 15 Feb 2023 15:54:34 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1586920740142-346aea2a2124?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDE2fHxjb21wb25lbnR8ZW58MHx8fHwxNjc2NDU0Nzg3&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded/></item><item><title><![CDATA[Widget Architecture - Part 2]]></title><description><![CDATA[Converting prototype component system to data-driven w/ live reloading & automation using Sourcery for easier maintenance & reliability]]></description><link>https://www.merowing.info/widget-architecture-part-2/</link><guid isPermaLink="false">63cd74e7ad2da9003d9c5c8a</guid><category><![CDATA[Architecture]]></category><category><![CDATA[Workflow]]></category><category><![CDATA[Swift]]></category><dc:creator><![CDATA[Krzysztof Zabłocki]]></dc:creator><pubDate>Mon, 23 Jan 2023 13:27:39 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1489850846882-35ef10a4b480?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDF8fGplbmdhfGVufDB8fHx8MTY3NDQxODQ5OQ&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1489850846882-35ef10a4b480?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDF8fGplbmdhfGVufDB8fHx8MTY3NDQxODQ5OQ&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Widget Architecture - Part 2"><p><a href="https://www.merowing.info/widget-based-architecture-part-1/">Last time</a>, we established a foundational architecture for composable and self-contained widgets. This week, we will convert that prototype into a data-driven structure. </p><p>To enhance the development experience, we will integrate live reload functionality by utilizing the <a href="https://github.com/krzysztofzablocki/KZFileWatchers?ref=merowing.info">FileWatcher library</a>. </p><p>Furthermore, we will implement an automated registration system using <a href="https://github.com/krzysztofzablocki/Sourcery?ref=merowing.info">Sourcery</a> to ensure that newly added widgets are immediately available for use within the project.</p><figure class="kg-card kg-video-card kg-card-hascaption"><div class="kg-video-container"><video src="https://www.merowing.info/content/media/2023/01/ezgif.com-gif-maker-2.mp4" poster="https://img.spacergif.org/v1/1312x838/0a/spacer.png" width="1312" height="838" playsinline preload="metadata" style="background: transparent url(&apos;https://www.merowing.info/content/images/2023/01/media-thumbnail-ember325.jpg&apos;) 50% 50% / cover no-repeat;"></video><div class="kg-video-overlay"><button class="kg-video-large-play-icon"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/></svg></button></div><div class="kg-video-player-container"><div class="kg-video-player"><button class="kg-video-play-icon"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/></svg></button><button class="kg-video-pause-icon kg-video-hide"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><rect x="3" y="1" width="7" height="22" rx="1.5" ry="1.5"/><rect x="14" y="1" width="7" height="22" rx="1.5" ry="1.5"/></svg></button><span class="kg-video-current-time">0:00</span><div class="kg-video-time">/<span class="kg-video-duration"></span></div><input type="range" class="kg-video-seek-slider" max="100" value="0"><button class="kg-video-playback-rate">1&#xD7;</button><button class="kg-video-unmute-icon"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M15.189 2.021a9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h1.794a.249.249 0 0 1 .221.133 9.73 9.73 0 0 0 7.924 4.85h.06a1 1 0 0 0 1-1V3.02a1 1 0 0 0-1.06-.998Z"/></svg></button><button class="kg-video-mute-icon kg-video-hide"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M16.177 4.3a.248.248 0 0 0 .073-.176v-1.1a1 1 0 0 0-1.061-1 9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h.114a.251.251 0 0 0 .177-.073ZM23.707 1.706A1 1 0 0 0 22.293.292l-22 22a1 1 0 0 0 0 1.414l.009.009a1 1 0 0 0 1.405-.009l6.63-6.631A.251.251 0 0 1 8.515 17a.245.245 0 0 1 .177.075 10.081 10.081 0 0 0 6.5 2.92 1 1 0 0 0 1.061-1V9.266a.247.247 0 0 1 .073-.176Z"/></svg></button><input type="range" class="kg-video-volume-slider" max="100" value="100"></div></div></div><figcaption>Live reloading data driven widgets&#xA0;</figcaption></figure>]]></content:encoded></item><item><title><![CDATA[Widget architecture - part 1]]></title><description><![CDATA[Let's start exploring a component-based architecture slightly different from the usual patterns we use.]]></description><link>https://www.merowing.info/widget-based-architecture-part-1/</link><guid isPermaLink="false">6384dd50507777003d26ee3a</guid><category><![CDATA[Workflow]]></category><category><![CDATA[Swift]]></category><category><![CDATA[Architecture]]></category><dc:creator><![CDATA[Krzysztof Zabłocki]]></dc:creator><pubDate>Mon, 05 Dec 2022 12:45:20 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1541692641319-981cc79ee10a?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDF8fGJsb2Nrc3xlbnwwfHx8fDE2NzAyNDQzNjE&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1541692641319-981cc79ee10a?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDF8fGJsb2Nrc3xlbnwwfHx8fDE2NzAyNDQzNjE&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Widget architecture - part 1"><p>This series of articles will explore a component-based architecture, in which each component or widget is self-contained, allowing for quick scaling of a project by adding more engineers. </p><p>This type of architecture has been used in several client projects, such as powering generic list content at <a href="https://www.nytimes.com/?ref=merowing.info">The New York Times</a> and creating new home widgets at <a href="https://thebrowser.company/?ref=merowing.info">The Browser Company</a>.</p><h2 id="requirements">Requirements</h2><p>At the end of the series, we&apos;ll have an architecture in which</p><ul><li>Widgets are self-contained and can be worked on in isolation</li><li>Adding new widgets doesn&apos;t require any manual labor from the developers</li><li>We support the rapid development of new and existing widgets</li><li>Widgets can be driven via data</li></ul>]]></content:encoded></item><item><title><![CDATA[Debugging Tips]]></title><description><![CDATA[What are your favorite Swift debugging tips?]]></description><link>https://www.merowing.info/swift-debugging-tips/</link><guid isPermaLink="false">6371261be1758b004dde086e</guid><category><![CDATA[Workflow]]></category><dc:creator><![CDATA[Krzysztof Zabłocki]]></dc:creator><pubDate>Mon, 14 Nov 2022 12:00:19 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1582845512747-e42001c95638?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fGRlYnVnfGVufDB8fHx8MTY2ODQwMzUwMQ&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1582845512747-e42001c95638?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fGRlYnVnfGVufDB8fHx8MTY2ODQwMzUwMQ&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Debugging Tips"><p>Here are a couple of my favorite debugging tricks and tips I use while working on Swift projects.</p><h2 id="configure-your-lldbinit">Configure your .lldbinit</h2><p>First, most of us want to work with Swift, not Objective-C, but depending on your project setup, you might have Objective-C enabled by default. We have 2 options:</p><ol><li>We can manually change the language during the lldb session by calling<code>settings set target.language swift</code> </li><li>We can create a <code>.lldbinit</code> file in our home directory and add it there to be the default for all debugging sessions e.g. <code>echo &apos;settings set target.language swift&apos; &gt; ~/.lldbinit</code> followed by <code>chmod +x ~/.lldbinit</code></li></ol><p>Furthermore, .lldbinit is a great place to add additional things that you&apos;ll be using across your projects. Here is a part of mine:</p><pre><code class="language-swift">settings set target.language swift

breakpoint set -r NSWindow.initialFirstResponder --one-shot true --auto-continue true
breakpoint command add
e import AppKit
e import Foundation
e func $vc&lt;T&gt;(_ input: T) -&gt; NSViewController { unsafeBitCast(input, to: NSViewController.self) }
e func $view&lt;T&gt;(_ input: T) -&gt; NSView { unsafeBitCast(input, to: NSView.self) }
DONE

breakpoint set -n UIApplicationMain --one-shot true --auto-continue true
breakpoint command add
e import UIKit
e import Foundation
e func $vc&lt;T&gt;(_ input: T) -&gt; UIViewController { unsafeBitCast(input, to: UIViewController.self) }
e func $view&lt;T&gt;(_ input: T) -&gt; UIView { unsafeBitCast(input, to: UIView.self) }
DONE</code></pre><p>This allows me to use memory address to get info about my types easily:</p><p><code>po $vc(0x128027ad400)</code></p><p>Notes:</p><ul><li>Using <strong>$</strong> for variable and function names is how we get those things available outside of just the current expression context, from Apple:</li></ul><blockquote>If the first character of your user defined variable is a $, then the variable&apos;s value will be available in future expressions, otherwise it will just be available in the current expression.</blockquote><ul><li>We configure the initial breakpoint as a trigger for adding new functions into the system. Otherwise, they wouldn&apos;t work since expressions aren&apos;t evaluated as part of lldb initialization due to lack of stack frames</li><li>I work in both Mac and iOS context so I set 2 separate breakpoints and variants of the common functions I use</li></ul><h2 id="leverage-frame-variables">Leverage frame variables</h2><p>Most Swift engineers are used to using <code>print object</code> or <code>po</code> for short, but there is an alternative that often works faster and works in cases when <code>po</code> might fall short: <code>frame variable</code> or <code>v</code></p><p>The short alias was added back in Xcode 10.2, and here&apos;s Apple note about it</p><blockquote>The LLDB debugger has a new command alias, <strong>v</strong>, for the <strong>&#x201C;frame variable&#x201D; </strong>command to print variables in the current stack frame. Because it bypasses the expression evaluator, v can be a lot faster and should be preferred over <strong>p</strong> or <strong>po</strong>.<br><a href="https://developer.apple.com/documentation/xcode_release_notes/xcode_10_2_release_notes?ref=merowing.info" rel="nofollow">https://developer.apple.com/documentation/xcode_release_notes/xcode_10_2_release_notes</a> </blockquote><div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-emoji">&#x1F914;</div><div class="kg-callout-text"><code>v</code> and <code>vo</code> works for stored properties but won&apos;t work for computed ones. You&apos;ll need <code>po</code> for those.</div></div><p>Here&apos;s an example:</p><figure class="kg-card kg-image-card"><img src="https://pbs.twimg.com/media/E1LCb_PXsAMMk-T?format=png&amp;name=900x900" class="kg-image" alt="Debugging Tips" loading="lazy"></figure><p>You can add additional flags to it to get even more information by adding <strong>-O </strong>option to it.</p><h2 id="use-expressions">Use expressions</h2><p>The aforementioned <strong><code>po</code> </strong>is an alias for a <strong><code>e -O &#x2013;</code> </strong>which will evaluate the object and try to call <code>description</code> method on it if it exists, it will evaluate the given expression and then try calling <code>description</code> method on it.</p><p>But expressions are more valuable than that, and it would be worth using them directly instead of relying on <code><strong>po</strong></code>.</p><p>We can interact with our system by leveraging <code>e</code> or <code>expression</code>, this can be very convenient when dealing with variables:</p><pre><code class="language-swift">e var $vc = self.controller
e $vc.view.layer.borderColor = CGColor.red
e CATransation.flush() // Refresh the core animation screen without having to end debugging session</code></pre><h2 id="observe-the-system">Observe the system </h2><p>We can leverage breakpoints for many things, from changing default inputs into fields (e.g. login forms):</p><figure class="kg-card kg-image-card"><img src="https://www.merowing.info/content/images/2022/11/Screenshot-2022-11-14-at-05.27.22.png" class="kg-image" alt="Debugging Tips" loading="lazy" width="448" height="207"></figure><p>To using debugger commands to build symbolic breakpoint chains, e.g.</p><p><code>breakpoint set --name &quot;[CommandBarInputContainer layout]&quot;</code></p><p>Would create a breakpoint on the layout call of my NSView, now this would be too frequent, but I could set it up as part of another breakpoint execution, e.g. when the user changes the text input.</p><p>I could also use <code>--one-shot true</code> to only execute the breakpoint once per trigger.</p><h3 id="watch-variable-change">Watch variable change</h3><p>We can add breakpoints when a variable changes, either through Xcode UI or lldb command:</p><p><code>watchpoint set variable self.homeViewController</code></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.merowing.info/content/images/2022/11/Screenshot-2022-11-14-at-05.36.39.png" class="kg-image" alt="Debugging Tips" loading="lazy" width="371" height="397"><figcaption>Watchpoint UI</figcaption></figure><p>Then whenever that variable changes, Xcode will stop our debugging session and give us info like this:</p><figure class="kg-card kg-image-card"><img src="https://www.merowing.info/content/images/2022/11/Screenshot-2022-11-14-at-05.36.56.png" class="kg-image" alt="Debugging Tips" loading="lazy" width="207" height="80"></figure><p>We can then print and interact with that value</p><pre><code class="language-swift">po value
&#x25BF; Optional&lt;NSViewController&gt;
  &#x25BF; some : &lt;HomeButton.HomeViewController: 0x13407cf3ac0&gt;</code></pre><h2 id="what-are-your-favorites">What are your favorites?</h2><p>Let me know what your favorite tips or lldb commands are!</p><!--kg-card-begin: html--><div style="text-align:center"><a href="https://twitter.com/merowing_?ref_src=twsrc%5Etfw&amp;ref=merowing.info" class="twitter-follow-button" data-size="large" data-show-count="true">Follow @merowing_</a><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div><!--kg-card-end: html--><p></p>]]></content:encoded></item><item><title><![CDATA[TCA Performance and Multi-Store]]></title><description><![CDATA[Let's talk about using multi-store pattern in TCA as a way to improve the performance and maintainability.]]></description><link>https://www.merowing.info/multi-store-tca/</link><guid isPermaLink="false">633fd48ec7386b003d1bce31</guid><category><![CDATA[Architecture]]></category><category><![CDATA[The Composable Architecture]]></category><dc:creator><![CDATA[Krzysztof Zabłocki]]></dc:creator><pubDate>Mon, 10 Oct 2022 17:02:36 GMT</pubDate><media:content url="https://images.unsplash.com/reserve/oGLumRxPRmemKujIVuEG_LongExposure_i84.jpeg?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDE0fHxzcGVlZHxlbnwwfHx8fDE2NjUxMjgzODM&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/reserve/oGLumRxPRmemKujIVuEG_LongExposure_i84.jpeg?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDE0fHxzcGVlZHxlbnwwfHx8fDE2NjUxMjgzODM&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="TCA Performance and Multi-Store"><p>In previous articles, I discussed <a href="https://www.merowing.info/exhaustive-testing-in-tca/">Testability</a> and <a href="https://www.merowing.info/boundries-in-tca/">API Boundries</a> when using The Composable Architecture. This week I will dive into performance and a design pattern you might consider: multi-store. This pattern can improve performance and enforce rigid API boundaries.</p><h2 id="how-are-actions-processed-in-tca">How are actions processed in TCA?</h2><p>TCA implements a Unidirectional Data Flow (<strong>UDF</strong>). Using a UDF ensures that events and data in your application moves in a consistent and predictable manner. Following this pattern can reduce data inconsistencies because the source of truth for your application is the same throughout the whole application.</p><figure class="kg-card kg-image-card"><img src="https://www.merowing.info/content/images/2022/10/Flow.png" class="kg-image" alt="TCA Performance and Multi-Store" loading="lazy" width="1368" height="750" srcset="https://www.merowing.info/content/images/size/w600/2022/10/Flow.png 600w, https://www.merowing.info/content/images/size/w1000/2022/10/Flow.png 1000w, https://www.merowing.info/content/images/2022/10/Flow.png 1368w" sizes="(min-width: 720px) 720px"></figure><h4 id="udf-in-tca">UDF in TCA</h4><p>In TCA, the <code>State</code> managed by the root <code>Store</code> is the source of truth for your application. As different parts of the application need less and less of the overall application state, you use <code>scope</code> functions for whittling the <code>State</code> down into smaller components. One interesting aspect of the <code>State</code> is that it is not required to be <code>Equatable</code> , and there is no dependency graph through the application that is maintained automatically by TCA.</p><h4 id="why-is-this-important">Why is this important?</h4><p>Whenever we process actions in TCA, the architecture has to assume that the Root <code>State</code> has changed, and call all of our <code>scope</code> functions again and again.</p><p>Those scoping functions will be called tens of thousands of times during user sessions, and as such, you need to be careful what code you put in them. You should avoid any calculations in that function and have as simple operations as possible.</p><blockquote>Even sending a no-op action into TCA system will use significant cpu / main thread processing time because of all the scoping calls.</blockquote>]]></content:encoded></item><item><title><![CDATA[Composable Architecture @ Scale]]></title><description><![CDATA[<p>Last week I spoke at NSSpainX to talk about how to use Composable Architecture in larger projects, the kind of issues you might run into and how you can work around them.<br><br>Mentioned articles are:<br>- <a href="https://www.merowing.info/exhaustive-testing-in-tca/">Exhaustive testing in TCA</a><br>- <a href="https://www.merowing.info/boundries-in-tca/">TCA Action Boundaries</a><br>- <a href="https://www.merowing.info/tca-action-boundries-convenience/">TCA Action Boundries - Convenience</a></p>]]></description><link>https://www.merowing.info/composable-architecture-scale/</link><guid isPermaLink="false">632985d8d71d59003d7bf7a6</guid><category><![CDATA[Architecture]]></category><category><![CDATA[Talks]]></category><category><![CDATA[The Composable Architecture]]></category><dc:creator><![CDATA[Krzysztof Zabłocki]]></dc:creator><pubDate>Tue, 20 Sep 2022 09:22:37 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1475721027785-f74eccf877e2?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fGNvbmZlcmVuY2V8ZW58MHx8fHwxNjYzNjY1NzQ3&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1475721027785-f74eccf877e2?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fGNvbmZlcmVuY2V8ZW58MHx8fHwxNjYzNjY1NzQ3&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Composable Architecture @ Scale"><p>Last week I spoke at NSSpainX to talk about how to use Composable Architecture in larger projects, the kind of issues you might run into and how you can work around them.<br><br>Mentioned articles are:<br>- <a href="https://www.merowing.info/exhaustive-testing-in-tca/">Exhaustive testing in TCA</a><br>- <a href="https://www.merowing.info/boundries-in-tca/">TCA Action Boundaries</a><br>- <a href="https://www.merowing.info/tca-action-boundries-convenience/">TCA Action Boundries - Convenience</a></p><figure class="kg-card kg-embed-card"><iframe src="https://player.vimeo.com/video/751173570?h=9dc7f0ae02&amp;app_id=122963" width="426" height="240" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen title="1 - Composable Architecture at Scale - Krzysztof Zablocki"></iframe></figure>]]></content:encoded></item><item><title><![CDATA[Improving Developer Experience through tools and techniques]]></title><description><![CDATA[How to evaluate problem areas you see in day-to-day development to assess whether they could be solved at a scale of your team/company or the whole community?]]></description><link>https://www.merowing.info/improving-developer-experience-through-tools-and-techniques/</link><guid isPermaLink="false">631899bc70cb7b003dbd6145</guid><category><![CDATA[Mindset]]></category><category><![CDATA[Workflow]]></category><dc:creator><![CDATA[Krzysztof Zabłocki]]></dc:creator><pubDate>Wed, 07 Sep 2022 13:21:47 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1540575467063-178a50c2df87?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDF8fGNvbmZlcmVuY2V8ZW58MHx8fHwxNjYyNTU2NjU0&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1540575467063-178a50c2df87?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDF8fGNvbmZlcmVuY2V8ZW58MHx8fHwxNjYyNTU2NjU0&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Improving Developer Experience through tools and techniques"><p>My talk from <a href="https://appdevcon.nl/?ref=merowing.info">AppDevConNL</a> is now available to watch to anyone interested.<br><br>Why should developer experience be one of the cornerstones of your architecture design?</p><p>How to evaluate problem areas you see in day-to-day development to assess whether they could be solved at a scale of your team/company or the whole community?</p><p>It&apos;s a practical look at tools and techniques that I used with many of my clients to save months of work and enable much faster iteration speeds.</p><figure class="kg-card kg-embed-card"><iframe src="https://player.vimeo.com/video/741816814?h=1829b098ef&amp;app_id=122963" width="426" height="240" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen title="IMPROVING DEVELOPER EXPERIENCE THROUGH TOOLS AND TECHNIQUES"></iframe></figure>]]></content:encoded></item></channel></rss>