<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Agastya Darma Blog</title>
        <link>https://www.agas.dev/blog</link>
        <description>Agastya Darma Blog</description>
        <lastBuildDate>Sun, 23 Nov 2025 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <item>
            <title><![CDATA[Why I Swung the Pendulum Back]]></title>
            <link>https://www.agas.dev/blog/why-i-swung-the-pendulum-back</link>
            <guid>https://www.agas.dev/blog/why-i-swung-the-pendulum-back</guid>
            <pubDate>Sun, 23 Nov 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Reflecting on the transition from Engineering Manager back to Software Engineer at Mercari Japan, and the growth found in stepping out of the comfort zone.]]></description>
            <content:encoded><![CDATA[<p>There is a common misconception in tech that the career ladder only goes in one direction: up. You become a senior engineer, then a lead, then a manager, and eventually a director.</p>
<p>For a long time, I followed that path. But recently, I asked myself a hard question: When was the last time I felt the pure joy of building?</p>
<p>As an Engineering Manager, I got used to having the answers. Or, at least, being the person who guided others to them. So, when I accepted a Software Engineer role at Mercari US and moved to Japan, I wasn't just changing time zones. I was changing who I was at work. And honestly? It feels like taking a deep breath after holding it for years.</p>
<p>But growth requires discomfort. I live in Ginza, surrounded by stunning autumn leaves during the current season and incredible food, yet I feel the friction of the unfamiliar. I don't speak Japanese yet. I went from being a manager who communicated for a living to a resident who struggles to order dinner. It’s humbling. It reminds me that expertise is contextual. In the code, I am fluent. On the street, I am a toddler.</p>
<p>The hardest adjustment isn't the job or the language. It’s the silence in my apartment. My girlfriend is back in Indonesia. People say, "Distance makes the heart grow fonder," but mostly, it just makes you realize how much you miss the small moments. Loving Tokyo is easy, but being here without her is the tricky part.</p>
<p>I’m realizing that my "new chapter" isn't just about working or living in Japan. It’s about learning to integrate these fragmented parts of myself...</p>]]></content:encoded>
            <category>career</category>
            <category>japan</category>
            <category>engineering</category>
            <category>growth</category>
            <category>life</category>
        </item>
        <item>
            <title><![CDATA[Life Is A Single Player Game]]></title>
            <link>https://www.agas.dev/blog/life-is-a-single-player-game</link>
            <guid>https://www.agas.dev/blog/life-is-a-single-player-game</guid>
            <pubDate>Tue, 13 May 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Exploring the concept of life as a single-player game, inspired by Naval Ravikant, and how mastering internal mechanics leads to success in the multiplayer world.]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" src="https://oslo.shizu.id/81f60581-cff7-403c-9983-6a8fd04aa14b.jpeg" alt="Life Is A Single Player Game" class="img_ev3q"></p>
<br>
<p>Over the past few weeks, I have been working through <em>The Almanack of Naval Ravikant</em>, and it may be the most useful book I have read this year. One idea in particular keeps echoing: Naval's claim that life is, at its core, a single-player game.</p>
<p>The phrase is provocative because it ­repositions the burden of progress. In a single-player game the decisive moves are internal while the external world provides only the scenery and occasional obstacles. Society, by contrast, trains us for the multiplayer mode: we learn to seek status, negotiate group norms, and read social cues. Both modes matter, but the multiplayer emphasis can obscure the obvious that the joystick still rests in your own hands.</p>
<p>Framing life as primarily single-player doesn't deny community but for me it clarifies responsibility. You cultivate judgment, discipline, and original thought first, then bring those assets to the multiplayer arena. Seen this way, self-development is not self-indulgence but it is the prerequisite for meaningful contribution. Naval's point is simple but radical: master the single-player mechanics, and the multiplayer rounds take care of themselves.</p>
<blockquote>
<p>The reality is life is a single-player game. You're born alone. You're going to die alone. All of your interpretations are alone. All your memories are alone. You're gone in three generations, and nobody cares. Before you showed up, nobody cared. It's all single player.</p>
<p>Naval Ravikant</p>
</blockquote>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="subjectivity-of-reality">Subjectivity of Reality<a href="https://www.agas.dev/blog/life-is-a-single-player-game#subjectivity-of-reality" class="hash-link" aria-label="Direct link to Subjectivity of Reality" title="Direct link to Subjectivity of Reality" translate="no">​</a></h3>
<p>I once assumed reality was a single, unchanging snapshot identical for every observer. Naval showed me otherwise. A tree or a stone simply exists, but the moment I look at it, my mind overlays its own filter. My mood shifts the colors, and my memories supply the background score. What I perceive is no longer pure reality but reality plus me.</p>
<p>That insight was still humming in my head as I stepped out of Sinarmas MSIG Tower just after five-thirty, when the lobby still buzzed with people rushing for the elevators. My day at Ruangguru was done, but the commute was only beginning. I joined the steady stream of office workers moving toward the Dukuh Atas LRT. My backpack felt twice its usual weight: a laptop, a Mac mini I stubbornly insist on carrying, and a slim paperback titled <em>How to Love Someone Without Losing Your Mind</em>.</p>
<p>The platform was crowded, yet everything ran with the precision of habit—tap the card, file into the carriage, find any patch of floor space. When the doors closed, the train shuddered west to Bekasi. Outside, the skyline over Dukuh Atas shifted from solid blue to bands of orange and pink. Glass towers caught the light and flung it back in quick flashes.</p>
<p>Inside, most riders leaned into their phones. WhatsApp notifications popped up faster than thumbs could answer. Ride-hailing maps refreshed, showing red lines of traffic they were already planning to avoid. I opened my book instead, letting the first chapter pull my attention away from the carriage noise.</p>
<p>Same train, same rails, same sunset sliding past the windows yet every passenger was stitching together a different version of the evening. Jakarta reminded me of a truth I'd first learned months back in Tokyo, under winter neon and the razor-sharp air of Shibuya.</p>
<p><img decoding="async" loading="lazy" src="https://oslo.shizu.id/3cb96ff4-ba1c-4ef8-a208-ab980eb300c9.jpeg" alt="" class="img_ev3q"></p>
<p>It was a clear January night, one of those Tokyo nights when the air feels sharp enough to cut ribbon, and I was meeting my best friend, Gabriel, and his partner, Assyffa, in Shibuya. A couple of days earlier, they had shared big news: they were expecting their first child. Gabriel had kept himself on a strict no-drinking rule during that stretch, but now the restriction was lifted, and he was eager to mark the occasion. He ducked with me into the FamilyMart below their hotel, came back with two Strong Zeros, and pressed one into my hand as if it were a trophy.</p>
<p>We carried the cans upstairs to their room, which overlooked the famous crossing. From a couple of floors up, the intersection looked like a time-lapse animation: five crowds converging, dissolving, and reforming each time the lights flipped. Gabriel clinked his can against mine and took the first sip he'd allowed himself in a couple of months.</p>
<p>Same city, same minute, but radically different headspaces. Gabriel was already talking about life in general and how overjoyed they both felt. Soon  we were laughing about a mutual friend before pivoting somehow to raving about the unbelievably tasty Zunda shake from Sendai. I listened, nodded, and tried to match their joy, yet my mind hovered elsewhere on work in Jakarta, on a relationship that felt precarious, on the simple fact that after this toast, I would walk back to my place near Tokyo Tower alone.</p>
<p>Just before midnight, we said our goodbyes. Gabriel walked me to the station. I slipped through the underpass, boarded the last train bound for Onarimon, and let Official Hige Dandism's "Pretender" loop in my earbuds. I didn't yet know the woman I'd been seeing in Jakarta would end things a couple of weeks after I landed home, but the chorus, Kimi no unmei no hito wa boku ja nai ("I am not the one"), already felt uncomfortably accurate.</p>
<p>That night offered a clear lesson: we all breathed the same January air, drank the same cans of Strong Zero, and watched the same neon sweep of Shibuya Crossing, yet the meaning each of us attached to it was private. For Gabriel and Assyffa, Tokyo was an opening scene in the story of new life. For me, it was an interlude between an uncertain present and an even murkier return. Reality, I realized once again, is never a single photograph, but it is the raw image plus the caption each of us writes in our head.</p>
<p>Moments like these convince me that meaning isn't served to us on a silver platter, but we make it ourselves. That can feel terrifying, the way a blank page feels to a writer, until you remember a blank page is also pure freedom. Most of us first notice this freedom when life refuses to follow the checklist.</p>
<p>Picture a production bug that passes every test suite, ships, then appears again after the fifth "final" patch. You watch logs, rerun scripts, and wonder how many more late nights it will devour. In moments like this, the outside world is fixed, and you cannot wish the defect away, but your inner narrative is still malleable. You can label the bug a catastrophe, or you can treat it as the tuition you pay for mastery.</p>
<p>The same principle scales. A project timeline slips, a meeting derails, a teammate quits, and suddenly your sprint looks like spilled coffee. Reality hands you raw facts; interpretation is up to you. Between what happens and what you decide it means lies a narrow gap. Inside that gap you choose whether to react with frustration or curiosity, whether to blame circumstances or refine your craft</p>
<p>Calm focus starts here. When you recognize that the story is yours to edit, the blank page stops feeling hostile. It becomes a drafting table. Each thought you revise, each perspective you tighten, is another sentence in the manual you write for yourself. That manual is where real agency lives, and no external setback can seize it.</p>
<p>I picture life as a single-player adventure running on imperfect code. We don't pick our spawn point, birthplace, skin, those lines were compiled long before we pressed "Start." Yet the controller is still in our hands, and even the glitches can become hidden shortcuts. If I keep my view wide enough to marvel at the world's secret art direction and sharp enough to read the warning signs etched in its walls, every new level stops feeling random and starts feeling like an invitation to master its mechanics.</p>
<p>To be continued…</p>]]></content:encoded>
            <category>personal</category>
            <category>growth</category>
            <category>self-improvement</category>
            <category>mindfulness</category>
            <category>lifestyle</category>
            <category>spirituality</category>
        </item>
        <item>
            <title><![CDATA[Love, Luck, and Four-Leaf Clovers]]></title>
            <link>https://www.agas.dev/blog/love-luck-and-four-leaf-clovers</link>
            <guid>https://www.agas.dev/blog/love-luck-and-four-leaf-clovers</guid>
            <pubDate>Sat, 29 Mar 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Exploring love, luck, and the symbolism of four-leaf clovers, reflecting on meaningful connections and unexpected blessings.]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" src="https://oslo.shizu.id/63530912-7b7d-4d6d-a6ab-9959d568fd0c.jpeg" alt="Four-Leaf Clover" class="img_ev3q"></p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="audio-overview">Audio Overview<a href="https://www.agas.dev/blog/love-luck-and-four-leaf-clovers#audio-overview" class="hash-link" aria-label="Direct link to Audio Overview" title="Direct link to Audio Overview" translate="no">​</a></h4>
<!-- -->
<div style="max-width:230px;width:640px;height:50px"></div>
<br>
<p>Recently, I stumbled upon the enchanting mystery of the four-leaf clover through someone who once brightened my world. During our last date, she gently introduced me to her affection for clovers, especially the rare and elusive four-leaf variety, and her gentle fascination quickly became my own. I'd always thought of four-leaf clovers simply as symbols of luck, but seeing them through her eyes revealed their deeper charm and beauty. It's astonishing how certain people quietly enter our lives and change the way we see things.</p>
<p>To me, she was the very essence of a four-leaf clover: wonderfully rare, quietly captivating, and softly unforgettable. Though our paths have now parted and we no longer speak, her genuine love for this delicate natural wonder left a lasting impression. I found myself drawn deeper into exploring the beautiful mysteries and history behind the symbolism of the four-leaf clover.</p>
<!-- -->
<p>Guided by newfound curiosity, I unearthed fascinating details about the four-leaf clover. Each leaf beautifully symbolizes hope, faith, love, and luck, adding profound depth to my personal connection with this small, meaningful plant. It's more than mere luckk, for me it represents a heartfelt optimism and positivity.</p>
<p>Throughout history, the four-leaf clover has been revered across cultures as a powerful talisman. From ancient Egyptians blessing marriages to Celtic Druids who saw it as protection against negativity, the universal positivity surrounding this plant made me appreciate its simplicity and depth even more.</p>
<p>Meeting her felt exactly like discovering a rare four-leaf clover hidden among thousands of ordinary leaves, unexpectedly beautiful and deeply cherished. For me our encounter was a rare detour from the ordinary rhythms of life, leaving a profound, lasting impact on me.</p>
<p>Though our conversations have faded into silence, her heartfelt wishes linger with me. Just as the four-leaf clover symbolizes lasting positivity, her kind thoughts and quiet hopes have flourished into unexpected joys and blessings in my life. This reflection has taught me the enduring impact that brief yet meaningful connections can have.</p>
<p>This year, 2025, has held an unexpected abundance of blessings for me. Reflecting on it now, I feel deeply fortunate partly due to her gentle wishes and my newly awakened fascination with the four-leaf clover. While initially saddened by our separation, I've found warmth and reassurance in the symbolism of this precious plant.</p>
<p>Just as discovering one four-leaf clover often hints at more nearby, uncovering luck in one aspect of my life has encouraged optimism and openness towards the future. Sometimes, hope and fortune reach us through surprising avenues, reminding us to trust life's mysterious pathways.</p>
<p>As I look forward, guided by the symbolism of hope, faith, love, and luck, I'm reminded to remain open-hearted and curious. Each relationship, experience, and subtle discovery holds the potential to enrich and deepen our journey.</p>
<p>To the one who introduced me to this beautiful symbol, thank you. Though our paths no longer intertwine, the quiet beauty and meaningful lessons you shared continue to blossom within my heart.</p>
<p>Just like the four-leaf clover, life offers rare moments of beauty and promise. If we pause, look gently, and remain hopeful, perhaps our very own four-leaf clover is waiting patiently for us to discover it.</p>
<hr>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="four-leaf-clover">Four-Leaf Clover<a href="https://www.agas.dev/blog/love-luck-and-four-leaf-clovers#four-leaf-clover" class="hash-link" aria-label="Direct link to Four-Leaf Clover" title="Direct link to Four-Leaf Clover" translate="no">​</a></h4>
<p>By Ella Higginson</p>
<blockquote>
<p>I know a place where the sun is like gold,
And the cherry blooms burst with snow,
And down underneath is the loveliest nook,
Where the four-leaf clovers grow.</p>
<p>One leaf is for hope, and one is for faith,
And one is for love, you know,
And God put another in for luck—
If you search, you will find where they grow.</p>
<p>But you must have hope, and you must have faith,
You must love and be strong – and so—
If you work, if you wait, you will find the place
Where the four-leaf clovers grow.</p>
</blockquote>]]></content:encoded>
            <category>love</category>
            <category>luck</category>
            <category>symbolism</category>
            <category>personal</category>
            <category>reflection</category>
            <category>gratitude</category>
            <category>connections</category>
        </item>
        <item>
            <title><![CDATA[Lessons From a Lotus]]></title>
            <link>https://www.agas.dev/blog/lessons-from-a-lotus</link>
            <guid>https://www.agas.dev/blog/lessons-from-a-lotus</guid>
            <pubDate>Fri, 28 Mar 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Discovering personal growth, resilience, and transformation through the symbolism of the lotus flower, emerging from life's challenges with clarity and authenticity.]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" src="https://oslo.shizu.id/d9490817-64f9-4534-8daf-40368570fa12.jpeg" alt="Lessons From a Lotus" class="img_ev3q"></p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="audio-overview">Audio Overview<a href="https://www.agas.dev/blog/lessons-from-a-lotus#audio-overview" class="hash-link" aria-label="Direct link to Audio Overview" title="Direct link to Audio Overview" translate="no">​</a></h4>
<!-- -->
<div style="max-width:230px;width:640px;height:50px"></div>
<br>
<p>The lotus flower has always captivated me, not only due to its visual beauty but also because of the profound symbolism it embodies. Gracefully emerging from murky, muddy waters, the lotus symbolizes personal growth, resilience, and transformation, representing the journey toward achieving clarity even amidst life's most challenging situations. Its beauty reminds us that significant strength and wisdom frequently come from navigating and overcoming difficult circumstances.</p>
<blockquote>
<p>“In the mud lies the promise of blooming. And for me, that promise has never been clearer.”</p>
</blockquote>
<p>Over the past month, my own life has mirrored this metaphor in profound ways. I’ve encountered a series of powerful experiences and pivotal moments that have challenged my previous perspectives, illuminating essential truths. These experiences, though often uncomfortable, have been instrumental in shaping a deeper understanding of myself and the world around me. Each lesson has guided me to question and eventually release certain outdated beliefs and patterns, making room for growth, authenticity, and genuine happiness.</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="speaking-negatively">Speaking Negatively&nbsp;<a href="https://www.agas.dev/blog/lessons-from-a-lotus#speaking-negatively" class="hash-link" aria-label="Direct link to Speaking Negatively&nbsp;" title="Direct link to Speaking Negatively&nbsp;" translate="no">​</a></h3>
<p>I used to believe that venting negative feelings about past experiences, whether in relationships, friendships, or even professional connections, would help me move forward. However, I've come to realize that speaking negatively about someone or something I once valued only prolongs bitterness and resentment. For instance, many of my friends have suggested that moving on from a painful situation requires holding onto anger or resentment, believing that negativity would empower me to break free. Yet, I've learned that carrying negativity only holds me back further. Genuine forgiveness, quiet respect, and letting go with grace are far more powerful in fostering true healing and personal growth.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="problems-without-solutions">Problems without Solutions<a href="https://www.agas.dev/blog/lessons-from-a-lotus#problems-without-solutions" class="hash-link" aria-label="Direct link to Problems without Solutions" title="Direct link to Problems without Solutions" translate="no">​</a></h3>
<p>At times, life's difficulties seemed impossible to overcome, leaving me feeling stuck, especially when these issues were beyond my direct control. Recently, I encountered a significant challenge that threatened my livelihood due to external factors at work. Initially, this issue appeared overwhelming, as it stemmed from circumstances entirely outside of my influence. However, I've learned that every challenge carries within it a hidden solution, even if the solution isn't immediately clear or straightforward. For example, when your company struggles and job security becomes uncertain, the immediate response might be to contribute your best efforts to help improve the situation. However, if the challenges exceed your abilities or control, the solution might involve taking uncomfortable yet necessary proactive steps, such as seeking new job opportunities. In my situation, working in tech meant embracing this discomfort, revisiting job preparation strategies, diligently practicing coding through LeetCode, and thoroughly preparing for technical interviews to proactively address and overcome the obstacle.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="attachment">Attachment<a href="https://www.agas.dev/blog/lessons-from-a-lotus#attachment" class="hash-link" aria-label="Direct link to Attachment" title="Direct link to Attachment" translate="no">​</a></h3>
<p>I've discovered that attachment often stems from deep-rooted fears, such as the fear of loss, change, or uncertainty. When we cling too tightly to people, possessions, or specific outcomes, we unintentionally invite greater suffering into our lives because nothing in life remains static or permanent. Embracing life's constant changes and understanding its transient nature allows me to love genuinely without feeling the need to control or possess others. True peace and lasting happiness come from cultivating inner strength, acceptance, and self-awareness, rather than relying on external circumstances or relationships to fulfill or complete me.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="right-person-wrong-time">Right Person, Wrong Time<a href="https://www.agas.dev/blog/lessons-from-a-lotus#right-person-wrong-time" class="hash-link" aria-label="Direct link to Right Person, Wrong Time" title="Direct link to Right Person, Wrong Time" translate="no">​</a></h3>
<p>In the past, I often comforted myself with the belief that certain relationships failed because they occurred at the wrong time. This perspective gave me a way to rationalize disappointments and heartaches. However, I've since realized that every relationship, regardless of outcome, holds purpose and value.</p>
<p>Now, I understand that each encounter happens exactly when it should, providing lessons essential to my growth. Every interaction, whether joyful or painful, has contributed meaningfully to shaping who I am today. Thus, I've come to appreciate that there truly is no such thing as a "wrong time," only experiences that help us evolve.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="finding-wholeness-in-another-person">Finding Wholeness in Another Person<a href="https://www.agas.dev/blog/lessons-from-a-lotus#finding-wholeness-in-another-person" class="hash-link" aria-label="Direct link to Finding Wholeness in Another Person" title="Direct link to Finding Wholeness in Another Person" translate="no">​</a></h3>
<p>I no longer believe that another person can "complete" me. For a long time, I thought finding someone special would fill all my inner voids, making me whole and content. But repeatedly, this belief led only to disappointment and dissatisfaction because it placed unrealistic expectations on others to fix what was missing within me.</p>
<p>Instead, I've discovered the importance of cultivating self-love and focusing on my own personal growth first. By investing in myself, becoming self-aware, and developing inner fulfillment, I've learned that healthy and meaningful relationships naturally emerge when two individuals come together as fully realized, secure people. These connections are richer, deeper, and more lasting because each person contributes equally and authentically, rather than relying on one another to fill emotional gaps.</p>
<hr>
<p>Much like the lotus flower, which grows beautifully from muddy waters, we too have the capacity to rise above challenging circumstances and transform our lives. By recognizing and releasing beliefs, behaviors, and emotional attachments that restrict our growth, we can open ourselves up to new perspectives and deeper understanding.</p>
<p>Embracing change, however uncomfortable it may be, enables us to stay authentic to our true selves, fostering genuine happiness and lasting peace. Through this courageous journey of self-discovery and acceptance, we not only overcome past struggles but also become stronger, wiser, and more fulfilled individuals.</p>]]></content:encoded>
            <category>personal</category>
            <category>growth</category>
            <category>self-improvement</category>
            <category>mindfulness</category>
            <category>lifestyle</category>
            <category>spirituality</category>
        </item>
        <item>
            <title><![CDATA[Why More Isn't Always Better]]></title>
            <link>https://www.agas.dev/blog/why-more-isnt-always-better</link>
            <guid>https://www.agas.dev/blog/why-more-isnt-always-better</guid>
            <pubDate>Mon, 24 Mar 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Exploring the hedonic treadmill and why constantly chasing "more" doesn't lead to lasting happiness.]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" src="https://oslo.shizu.id/152360bf-7484-41a9-bb97-929640e5d46b.jpeg" alt="Hedonic Treadmill" class="img_ev3q"></p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="audio-overview">Audio Overview<a href="https://www.agas.dev/blog/why-more-isnt-always-better#audio-overview" class="hash-link" aria-label="Direct link to Audio Overview" title="Direct link to Audio Overview" translate="no">​</a></h4>
<!-- -->
<div style="max-width:230px;width:640px;height:50px"></div>
<br>
<p>A few years ago, I realized something unsettling that no matter what I achieved or acquired, the satisfaction was always temporary. I’d set a goal, work hard, reach it, and feel an initial surge of excitement but only to find myself asking, “What’s next?” almost immediately.</p>
<p>At first, I thought this was just ambition. But the more I paid attention, the more I noticed a pattern that was leaving me perpetually unsatisfied. Turns out, there’s a name for this. The hedonic treadmill which are the idea that no matter what happens, we eventually return to a baseline level of happiness, always craving more and more. If you’ve ever felt stuck in this cycle, you’re not alone.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-never-ending-chase">The Never-Ending Chase<a href="https://www.agas.dev/blog/why-more-isnt-always-better#the-never-ending-chase" class="hash-link" aria-label="Direct link to The Never-Ending Chase" title="Direct link to The Never-Ending Chase" translate="no">​</a></h3>
<p>I remember the first time I convinced myself this thing would finally make me happy. It was a brand new phone, the one I had been obsessing over for months. When I finally got it, I was thrilled but after a few weeks, it became just another object in my life. The excitement had vanished, and I was already thinking about what else I wanted next.</p>
<p>The same thing happened with achievements. I worked hard to land my "dream job," thinking it would feel like I had arrived. Yet, just a few months in, I found myself setting new goals, feeling just as restless as before.</p>
<p>This is the hedonic treadmill in action. We convince ourselves that once we get the next promotion, the next gadget, or the next trip, we’ll finally feel satisfied. But as soon as we achieve it, our mind moves the goalpost. And the chase starts all over again.</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-social-media-trap">The Social Media Trap<a href="https://www.agas.dev/blog/why-more-isnt-always-better#the-social-media-trap" class="hash-link" aria-label="Direct link to The Social Media Trap" title="Direct link to The Social Media Trap" translate="no">​</a></h3>
<p>The problem was worsened by social media. Every time I opened Instagram, I saw people traveling to exotic places, buying luxury cars, and living what appeared to be their "best lives." It was an endless highlight reel, making me feel like I was falling behind and I needed to do more, have more, be more .</p>
<p>This created an unhealthy urgency. I started making decisions based on what I thought success “should” look like, rather than what actually made me happy. It felt like a race, one where I needed to catch up to everyone else who seemed to be winning at life.</p>
<p>At one point, I had a thought that really shook me: What happens if I rush through all the "big life moments" too soon? If I checked off all the major milestones in my 20s—traveling to dream destinations, buying expensive things, chasing achievements, would I still have anything meaningful to look forward to? The idea of reaching a point where nothing felt exciting anymore was terrifying.</p>
<p><strong>That’s when I realized that living life as a speedrun wasn’t making me happy. It was just making me exhausted.</strong></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="learning-to-slow-down">Learning to Slow Down<a href="https://www.agas.dev/blog/why-more-isnt-always-better#learning-to-slow-down" class="hash-link" aria-label="Direct link to Learning to Slow Down" title="Direct link to Learning to Slow Down" translate="no">​</a></h3>
<p>The turning point came when I started making intentional choices instead of impulsive ones. Instead of constantly upgrading my life at every opportunity, I embraced the idea of pacing it out by leaving room for future excitement, rather than burning through it all at once.</p>
<p>Here’s what changed:</p>
<ul>
<li class="">
<p><strong>I stopped upgrading everything immediately.</strong> My first car after getting a job was nothing fancy. In fact, I didn’t even buy a car until I <em>actually</em> needed one. At the time, it felt like a smart financial decision, but looking back, it also taught me something valuable: the joy of gradual progress. If I had rushed to get a luxury car right away, it would have set an unsustainable expectation for what I needed to feel satisfied. By starting with something simple and practical, I was able to appreciate each upgrade over time, making every step feel more meaningful instead of just another checkbox on the path to "success."</p>
</li>
<li class="">
<p><strong>I traveled without intention at first, but I learned to pace myself.</strong> In my early 20s, I was eager to check off every bucket list destination as soon as I had the time and money to do so. I jumped at every opportunity to travel, thinking that experiencing as much as possible, as quickly as possible, would make my life feel more exciting and fulfilling. But after a while, I noticed that the thrill of each trip faded faster than I expected. I started wondering if I kept going at this pace, would I eventually reach a point where I had already "done it all" before I even had a chance to truly appreciate it? That realization shifted my mindset. Instead of rushing through experiences, I decided to be more intentional, spacing out my travels and choosing trips that held deeper meaning at different stages of my life. Now, I still have incredible places left to visit, and the excitement of looking forward to new adventures hasn’t diminished.</p>
</li>
<li class="">
<p><strong>I learned to enjoy the journey, not just the goal.</strong> For the longest time, I saw my accomplishments as trophies things to check off a list so I could prove to myself (and maybe others) that I was moving forward. But once I achieved something, the satisfaction was always fleeting, and I felt the pressure to chase the next milestone immediately. Over time, I started shifting my perspective. Instead of focusing solely on the finish line, I began finding joy in the process itself whether it was learning a new skill, improving at my work, or simply growing as a person. By embracing the <em>experience</em> rather than fixating on the outcome, I found a deeper and more lasting sense of fulfillment.</p>
</li>
</ul>
<p>This mindset shift completely changed how I approached life. Instead of expecting happiness to come from the next big thing, I started finding joy in the present without the pressure of constantly chasing more.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="final-thoughts">Final Thoughts<a href="https://www.agas.dev/blog/why-more-isnt-always-better#final-thoughts" class="hash-link" aria-label="Direct link to Final Thoughts" title="Direct link to Final Thoughts" translate="no">​</a></h3>
<p>Stepping off the hedonic treadmill doesn’t mean giving up ambition or settling for less. It just means understanding that more isn’t always better and that happiness isn’t something you’ll find at the finish line, because the finish line keeps moving.</p>
<p>True satisfaction comes from creating a life where joy isn’t just about achieving the next goal, but about appreciating where you are right now . If you’re constantly chasing, you’ll always feel behind. But if you learn to pace yourself, you’ll find that happiness isn’t something to be pursued but it’s something to be lived.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="recommended-resources">Recommended Resources<a href="https://www.agas.dev/blog/why-more-isnt-always-better#recommended-resources" class="hash-link" aria-label="Direct link to Recommended Resources" title="Direct link to Recommended Resources" translate="no">​</a></h3>
<ol>
<li class="">
<p><strong>How To Make The Most Out of Your 20s - Y Combinator</strong>: A YouTube video providing advice on maximizing personal and professional growth during your twenties. <a href="https://www.youtube.com/watch?v=H_XMqRhLhic" target="_blank" rel="noopener noreferrer" class="">https://www.youtube.com/watch?v=H_XMqRhLhic</a></p>
</li>
<li class="">
<p><strong>The Hedonic Treadmill - Personal MBA</strong>: An explanation of the hedonic treadmill effect and strategies for breaking free from its cycle. <a href="https://personalmba.com/hedonic-treadmill/" target="_blank" rel="noopener noreferrer" class="">https://personalmba.com/hedonic-treadmill/</a></p>
</li>
</ol>]]></content:encoded>
            <category>psychology</category>
            <category>happiness</category>
            <category>personal-development</category>
            <category>self-improvement</category>
            <category>lifestyle</category>
        </item>
        <item>
            <title><![CDATA[Let Them Leave]]></title>
            <link>https://www.agas.dev/blog/let-them-leave</link>
            <guid>https://www.agas.dev/blog/let-them-leave</guid>
            <pubDate>Sat, 22 Mar 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[True connections are a choice, not a fight. Letting go creates space for those who truly want to stay.]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" src="https://oslo.shizu.id/cfa13e54-2b2b-4211-b7d1-bcf15d8a83d5.jpeg" alt="Letting Go" class="img_ev3q"></p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="audio-overview">Audio Overview<a href="https://www.agas.dev/blog/let-them-leave#audio-overview" class="hash-link" aria-label="Direct link to Audio Overview" title="Direct link to Audio Overview" translate="no">​</a></h4>
<!-- -->
<div style="max-width:230px;width:640px;height:50px"></div>
<br>
<p>January 2025 was one of the challenging periods of my life. I found myself at a crossroads, faced with a difficult decision: should I fight to hold on to people who were ready to walk away, or should I let them go? At first, the thought of letting go felt like surrender, like giving up on something that once mattered. But as I reflected, I came to a life-changing realization when people choose to leave, it is not my responsibility to convince them to stay. True connections should never require force or persuasion, and holding onto someone who has already decided to leave only prolongs the inevitable. So, rather than clinging to what was slipping away, I chose to step back and let them go.</p>
<p>This perspective may seem counterintuitive at first, especially in a world that often emphasizes fighting for relationships, proving loyalty, and never giving up on people. However, I’ve learned that there's profound peace in allowing things to unfold naturally. <strong>Don’t fight to keep people who no longer want to be in your life.</strong></p>
<p>This mindset doesn’t just apply to romantic relationships but it extends to friendships, family ties, and even professional relationships. Time and experience have taught me that when someone decides to distance themselves, it’s often a reflection of where they are on their own journey, not a measure of your worth. Clinging to relationships out of fear, insecurity, or obligation only leads to unnecessary pain. Instead, accepting that not everyone is meant to stay forever frees us to focus on the relationships that are genuine, mutual, and fulfilling.</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-inevitable-tide">The Inevitable Tide<a href="https://www.agas.dev/blog/let-them-leave#the-inevitable-tide" class="hash-link" aria-label="Direct link to The Inevitable Tide" title="Direct link to The Inevitable Tide" translate="no">​</a></h3>
<p>Have you ever truly succeeded in holding on to someone who was determined to leave? Has actively fighting to keep a person in your life whether in a romantic relationship, friendship, or any other bond ever led to a lasting, fulfilling connection? More often than not, the answer is no. Dr. Orion Tabban puts it plainly: <strong>"Fighting to keep someone in your life is ineffective. It just doesn’t work."</strong> When someone has already made the decision to walk away, any attempt to block their exit only intensifies their desire to leave. The more you resist, the harder they push to break free.</p>
<p>Dr. Tabban compares this dynamic to being in a cult <strong>easy to get in, but difficult to escape.</strong> When people feel trapped, they don’t reconsider their decision to leave but they only fight harder to reclaim their freedom. This is why he advocates for the complete opposite approach: relationships should be <strong>"hard to get into and very easy to get out of."</strong> The exit should always be open, not as an invitation for people to leave, but as a sign that their presence in your life is a choice, not an obligation.</p>
<p>This perspective resonates deeply with me because I’ve found myself on both sides of this painful reality. I’ve been the one desperately clinging to someone who was slipping away, convincing myself that if I tried hard enough, they might change their mind. I’ve also been the one who felt suffocated by another person’s attempts to hold on to me when my heart was no longer in it. In both situations, the struggle only caused more harm. Holding on out of fear, guilt, or desperation never strengthens a relationship it only creates tension and resentment.</p>
<p>Even if you do manage to convince someone to stay, as Dr. Tabban explains, it’s often a <strong>"Pyrrhic victory"</strong> a short-term win that comes at too great a cost. They may remain in your life, but not because they truly want to be there. Instead, their presence is driven by guilt, pity, obligation, or even fear of hurting you. These emotions are fleeting and cannot sustain a meaningful connection. Sooner or later, the underlying desire to leave will resurface, and the cycle of conflict and heartbreak will begin again.</p>
<p>The truth is, authentic relationships cannot be forced. Love, friendship, and human connection must be freely given, not demanded or coerced. When we fight to keep someone who wants to leave, we are not preserving the relationship but we are merely postponing the inevitable. The most powerful, self-respecting choice we can make is to let go with grace. When someone chooses to walk away, it’s not a failure but it’s a redirection, making space for the right people who genuinely want to be in our lives.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="fighting-for-the-wrong-things">Fighting for the Wrong Things<a href="https://www.agas.dev/blog/let-them-leave#fighting-for-the-wrong-things" class="hash-link" aria-label="Direct link to Fighting for the Wrong Things" title="Direct link to Fighting for the Wrong Things" translate="no">​</a></h3>
<p>Often, our instinct to fight for a relationship isn’t just about love but it’s about fear. The fear of loss, the fear of rejection, and, in some cases, the fear that we are not enough. As Cara Bowen insightfully points out, we sometimes <strong>fight harder to be chosen than we fight for ourselves.</strong> We stay in relationships where we feel unappreciated, undervalued, or even disrespected, believing that if we just try harder and if we prove our loyalty, patience, and endurance maybe the other person will finally see our worth. But this is a painful illusion. <strong>Love, real love, "doesn’t require you to prove your fight. Love fights with you, not against you."</strong> In other words, the right person will stand beside you, not force you into a constant battle just to keep them in your life.</p>
<p>Bowen’s own experience highlights a common misunderstanding about love and effort. She recalls being accused of <strong>"not fighting hard enough"</strong> when a relationship ended, as if leaving was a sign of failure or weakness. But in reality, walking away is not always a form of surrender. <strong>It is often a redirection of our efforts and a conscious choice to fight for something different, something more aligned with our well-being.</strong> As she powerfully redefines it: <strong>"When I walked away from the relationship, I didn’t stop fighting. I just began fighting for something else. I stopped fighting for it to work out no matter what and started fighting for what was best for both of us."</strong> This shift in mindset is incredibly liberating. Instead of expending all of our energy trying to hold onto someone who is drifting away, we can redirect that strength toward something far more meaningful—ourselves.</p>
<p><strong>Fighting for love does not mean sacrificing our own worth in the process.</strong> A relationship should never require one person to constantly prove their value while the other remains distant or uninterested. Love should be mutual, reciprocal, and full of shared effort. If we find ourselves in a dynamic where we are the only one holding on, it may be time to reevaluate what we are truly fighting for. Instead of desperately clinging to someone whose heart has already left, we can fight for <strong>our own self-respect, our well-being, and the love we genuinely deserve.</strong></p>
<p>At the end of the day, the hardest but most rewarding battle we will ever fight is the one for ourselves. Walking away from something that no longer serves us is not a sign of failure but it is an act of self-love and strength. The moment we stop chasing after people who don’t value us is the moment we free ourselves to welcome those who will.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-open-door-and-the-high-precipice">The Open Door and the High Precipice<a href="https://www.agas.dev/blog/let-them-leave#the-open-door-and-the-high-precipice" class="hash-link" aria-label="Direct link to The Open Door and the High Precipice" title="Direct link to The Open Door and the High Precipice" translate="no">​</a></h3>
<p>Dr. Orion Tabban offers a powerful analogy: imagine standing on the edge of a high cliff with no guardrail. The sheer awareness of the potential danger naturally makes people more cautious and mindful of their movements. In the same way, when people know that the <strong>"exit is always open and unobstructed,"</strong> they tend to be more conscious of their actions within a relationship. They won’t take your presence for granted if they understand that you won’t desperately fight to keep them there <strong>"irrespective of what they say or do."</strong> This creates a different kind of respect. One that isn’t driven by fear or obligation but by the understanding that the relationship is a choice, freely made by both individuals.</p>
<p>However, this doesn’t mean playing emotional games or creating artificial distance to manipulate someone into valuing you. It’s about recognizing your self-worth and understanding that you deserve to be with someone who genuinely chooses to be in your life. If someone frequently threatens to leave or tests your commitment by walking away, it’s essential to remember Dr. Tabban’s straightforward advice: <strong>"If someone actually leaves, like they literally walk out then never, ever, ever chase them down."</strong> The foundation of a healthy relationship should never require you to beg for someone's presence. According to Dr. Tabban, <strong>"The bare minimum required to resume a relationship is that they have to walk back in."</strong></p>
<p>In other words, love and commitment should be mutual. A person who truly values the relationship won't expect you to chase after them every time they choose to walk away. Instead of exhausting yourself trying to convince someone to stay, allow space for those who genuinely want to be there because the right people won’t need to be begged to remain in your life.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="fighting-for-your-heart">Fighting for Your Heart<a href="https://www.agas.dev/blog/let-them-leave#fighting-for-your-heart" class="hash-link" aria-label="Direct link to Fighting for Your Heart" title="Direct link to Fighting for Your Heart" translate="no">​</a></h3>
<p>At the core of a fulfilled and healthy life is one crucial battle which are the fight for our own hearts. This means recognizing our inherent worth, setting firm boundaries, and having the strength to walk away from relationships and situations that stifle our growth rather than nurture it. Too often, we exhaust ourselves fighting to be seen, valued, or chosen by others while neglecting the most important commitment of all, the one we owe to ourselves. As Cara Bowen beautifully puts it, <strong>"You are worth fighting for, but don’t forget to fight for your heart and to walk away from something that is suffocating the beauty that’s within you."</strong></p>
<p>Many people mistake letting go for giving up, but in reality, releasing something that no longer serves you is one of the bravest and most self-loving things you can do. Holding on to a connection that drains you, diminishes your self-worth, or fills you with constant insecurity does not bring happiness but it only prolongs pain. Walking away is not a sign of weakness; it is an act of profound self-respect. It is a declaration that you refuse to settle for less than what you deserve. By letting go, you create space not just for healing, but for new opportunities, new relationships, and deeper connections with people who genuinely cherish and appreciate you.</p>
<p>So, if someone in your life begins to pull away or makes the decision to leave, take a moment to acknowledge your emotions, but resist the urge to fight for someone who no longer wants to stay. Instead, <strong>let them go with grace. Wish them well, express gratitude for the time you shared, and remind yourself that their departure is not a reflection of your worth.</strong> Sometimes, people leave because their role in your story has come to an end, and clinging to them only keeps you from discovering the beautiful chapters still ahead.</p>
<p>The love, respect, and loyalty you desire should never have to be begged for and it should be given willingly. The people who are meant to be in your life will remain not because you fought to keep them, but because they genuinely value and choose to stay. <strong>You are not meant to chase love but you are meant to receive it freely, wholeheartedly, and without condition.</strong> And when you begin to fight for yourself instead of fighting to hold on to others, you open the door to the kind of love and relationships that truly make life meaningful.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="recommended-reading-materials">Recommended Reading Materials<a href="https://www.agas.dev/blog/let-them-leave#recommended-reading-materials" class="hash-link" aria-label="Direct link to Recommended Reading Materials" title="Direct link to Recommended Reading Materials" translate="no">​</a></h3>
<ol>
<li class=""><strong>Episode 460: Don't fight to keep people (you lose when you win)</strong>: A podcast episode by Dr. Orion Tabban discussing the futility of fighting to keep someone in your life. It emphasizes the importance of letting go and allowing people to make their own choices. <a href="https://oriontaraban.podbean.com/e/episode-460/" target="_blank" rel="noopener noreferrer" class="">https://oriontaraban.podbean.com/e/episode-460/</a></li>
<li class=""><strong>You Shouldn't Have To Fight For Someone's Love</strong>: An article that highlights the importance of mutual effort and how love shouldn't feel like a constant battle. It encourages readers to recognize their worth and seek relationships where they are valued and appreciated. <a href="https://thought.is/you-shouldnt-have-to-fight-for-someones-love/" target="_blank" rel="noopener noreferrer" class="">https://thought.is/you-shouldnt-have-to-fight-for-someones-love/</a></li>
<li class=""><strong>Love Is Not The Same In Your Twenties, And That's A Good Thing</strong>: An article discussing how our understanding of love evolves as we mature. It suggests that recognizing this evolution can lead to healthier and more fulfilling relationships. <a href="https://thought.is/love-is-not-the-same-in-your-twenties-and-thats-a-good-thing/" target="_blank" rel="noopener noreferrer" class="">https://thought.is/love-is-not-the-same-in-your-twenties-and-thats-a-good-thing/</a></li>
</ol>]]></content:encoded>
            <category>personal</category>
            <category>growth</category>
            <category>relationships</category>
            <category>self-improvement</category>
        </item>
        <item>
            <title><![CDATA[Seek Discomfort]]></title>
            <link>https://www.agas.dev/blog/seek-discomfort</link>
            <guid>https://www.agas.dev/blog/seek-discomfort</guid>
            <pubDate>Fri, 21 Mar 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Embracing discomfort is the key to real growth. In an age of convenience, challenge yourself to break free and find true fulfillment.]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" src="https://oslo.shizu.id/98817f12-ca28-41af-a156-244d03d0fbf8.jpeg" alt="Seek Discomfort" class="img_ev3q"></p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="audio-overview">Audio Overview<a href="https://www.agas.dev/blog/seek-discomfort#audio-overview" class="hash-link" aria-label="Direct link to Audio Overview" title="Direct link to Audio Overview" translate="no">​</a></h4>
<!-- -->
<div style="max-width:230px;width:640px;height:50px"></div>
<br>
<p>We live in an era where effort is becoming optional.</p>
<p>Craving a meal? Open Gojek or Grab, and it will arrive at your doorstep in 20 minutes. Need new clothes? A few taps on Shopee, and they’ll be delivered the same day. Feeling too tired to talk to someone? Just send a text or voice note. Life’s daily inconveniences are disappearing, erased by technology and smart solutions designed to make everything seamless and effortless.</p>
<p>At first glance, this sounds amazing. Technology is here to enhance our lives, saving us time and freeing us from tedious work. But the more I look around, the more I wonder. What are we doing with this newfound freedom? Are we using it to grow, improve, and challenge ourselves? Or are we becoming complacent and drifting toward an existence where struggle is minimized, and comfort is prioritized above all else?</p>
<p>Boomers complain that Millennials and Gen Z are lazy, entitled, unwilling to put in the hard work their generation endured. Meanwhile, motivational entrepreneurs preach hustle culture, while self-care advocates insist we need to slow down. And now, AI is stepping into the picture—promising to make life even easier, anticipating our needs before we even recognize them ourselves.</p>
<p>With AI tools like NotebookLM assisting with research, coding aids like Cursor speeding up software development, and generative models drafting emails and writing content, the line between productivity and mindlessness is blurring. We are moving toward a society where friction is eliminated altogether.</p>
<p>But here’s the burning question that keeps me up at night: Is a life without struggle truly fulfilling?</p>
<p>I recently came across an insightful article by Julie Zhuo titled Our Souls Need Proof of Work, and it resonated with me deeply. The idea she explores is simple but profound: we are wired for effort. Without it, we lose purpose, resilience, and genuine happiness.</p>
<p>Without effort, without discomfort, we risk living an empty existence.</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-exactly-constitutes-hard-work">What exactly constitutes hard work?<a href="https://www.agas.dev/blog/seek-discomfort#what-exactly-constitutes-hard-work" class="hash-link" aria-label="Direct link to What exactly constitutes hard work?" title="Direct link to What exactly constitutes hard work?" translate="no">​</a></h3>
<p>In my view, hard work is deeply individual. It's whatever presents a genuine challenge to you personally and the tasks that push against your natural inclinations or comfort zones.</p>
<p>Some find it incredibly difficult to rise at 5 AM for an early morning jog, while others might accomplish this effortlessly but struggle terribly with speaking before crowds. Personally, exercising at the gym, running extended routes, and studying Mandarin all qualify as hard work for me. Each requires discipline and persistence that doesn't come naturally.</p>
<p>Certain individuals handle physically demanding tasks with remarkable ease but find mental activities extraordinarily challenging. Some excel effortlessly in social environments while others find them painfully uncomfortable. Hard work varies dramatically from person to person. It fundamentally involves pushing beyond your familiar boundaries into territory that demands more from you than you'd naturally give.</p>
<p>Therefore, consider this carefully: What genuinely challenges you? What makes you want to quit or avoid the task altogether?</p>
<p>Because that's precisely where your greatest opportunity for meaningful personal development exists. The resistance you feel marks the path toward your most significant growth.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="since-childhood-ive-always-longed-for-comfort">Since childhood, I’ve always longed for comfort.<a href="https://www.agas.dev/blog/seek-discomfort#since-childhood-ive-always-longed-for-comfort" class="hash-link" aria-label="Direct link to Since childhood, I’ve always longed for comfort." title="Direct link to Since childhood, I’ve always longed for comfort." translate="no">​</a></h3>
<p>As a kid, I found joy in simple things: new toys, sweet treats, and lazy Sundays spent on the couch watching cartoons. As I grew older, my desires became more refined but stayed the same at their core. A cozy home in a quiet neighborhood, a reliable car with comfortable seats, enough financial stability to buy what I wanted without worrying about my bank balance, and the freedom to travel wherever I wanted without stressing about vacation days or flight costs.</p>
<p>In Indonesia, people like me are called Anak Ambis (short for “ambitious kid”). I eagerly read articles and watched videos about tech professionals in Silicon Valley, fascinated by their luxurious homes in the California hills, their garages filled with the latest Tesla models, and their endless workplace perks, from gourmet cafeterias to nap pods. I often imagined myself in their position, planning my own path toward that life, where comfort wasn’t just something to have, it was a carefully designed way of living.</p>
<p>But there’s a reason people say, Be careful what you wish for. This phrase has lasted for generations because it carries an important truth.</p>
<p>Comfort is addictive. It can also be deceptive in ways you only notice once you’re already caught in it. Like quicksand disguised as soft carpet, it keeps pulling you in, step by step, making you believe you’re on solid ground <strong>until you realize you’re not.</strong></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-trap-of-ultra-convenience">The trap of ultra convenience<a href="https://www.agas.dev/blog/seek-discomfort#the-trap-of-ultra-convenience" class="hash-link" aria-label="Direct link to The trap of ultra convenience" title="Direct link to The trap of ultra convenience" translate="no">​</a></h3>
<p>Life is designed to eliminate friction. It's an invisible luxury woven into our daily routines, one we rarely question. Need food? Order online. Need a ride? Tap for a Gojek or Grab. Need entertainment? Scroll through an infinite feed of personalized recommendations. The modern world has systematically removed the small inconveniences that once punctuated our existence, replacing them with seamless digital solutions.</p>
<p>And it's only going to get easier. The AI revolution is leading us toward a world where our needs will be anticipated before we express them. Smart assistants will plan our schedules, draft our emails, summarize our meetings, even tell us which purchases we should consider. Hurdles that once required effort like thinking, planning, making decisions will quietly dissolve. We're approaching a reality where cognitive friction itself becomes obsolete, where algorithms silently handle the mental tasks we once struggled through.</p>
<p>At work, AI enhanced tools have already transformed my daily tasks. My productivity skyrocketed, but at the same time, there's a looming sense that something essential is slipping away. The satisfaction of solving complex problems, the growth that comes from overcoming obstacles, the pride in mastering difficult skills. These experiences feel increasingly rare in an optimized world.</p>
<p>When everything becomes easy, what is left to fight for? What becomes of human ambition when the path of least resistance is always illuminated? <strong>Perhaps the greatest paradox of our time is that in eliminating life's difficulties, we may be eliminating the very things that make life meaningful</strong>.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-sneaky-thief-of-fulfillment">The sneaky thief of fulfillment<a href="https://www.agas.dev/blog/seek-discomfort#the-sneaky-thief-of-fulfillment" class="hash-link" aria-label="Direct link to The sneaky thief of fulfillment" title="Direct link to The sneaky thief of fulfillment" translate="no">​</a></h3>
<p>Dopamine is the brain's anticipation chemical. It doesn't bring happiness, it fuels our desire for happiness. It's the neurological carrot dangling before us, pushing us forward with promises it never quite fulfills.</p>
<p>Every time we get what we want, dopamine surges but only for a moment. Then it crashes, leaving us craving the next hit. This biochemical rollercoaster explains why satisfaction is so fleeting and why we're constantly chasing the next reward.</p>
<p>Have you ever posted on TikTok and gotten 40 likes? At first, it feels amazing. Then, one day, one of your videos goes viral and you hit 10,000 likes! Incredible! But from that point on, every new post that gets just 200 likes feels disappointing, despite being five times your original benchmark for success.</p>
<p>Your expectations recalibrate with frightening efficiency. The neural pathways that once lit up at small victories now barely flicker unless stimulated by increasingly dramatic rewards. You can no longer derive joy from what once thrilled you like those 40 likes, that small promotion, or that modest achievement have become invisible, unremarkable. The hedonic treadmill accelerates beneath your feet, forcing you to run faster and faster just to maintain the same level of satisfaction, leaving you breathless in pursuit of diminishing emotional returns. Each new benchmark becomes ordinary with alarming speed.</p>
<p>Extreme comfort doesn't make us happier but i think it makes us numb. When we eliminate all challenges and discomfort from our lives, we inadvertently strip away the very experiences that give life its depth and meaning. The absence of struggle creates a peculiar void where satisfaction should be. Like muscles that atrophy without resistance, our capacity for joy weakens without occasional friction. We construct frictionless environments of convenience and ease, only to find ourselves drifting through them with a vague, unsettling sense that something essential is missing. The contrast necessary to appreciate the good moments, the small victories over adversity that once punctuated our days with genuine satisfaction.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="seeking-discomfort-for-real-growth">Seeking discomfort for real growth<a href="https://www.agas.dev/blog/seek-discomfort#seeking-discomfort-for-real-growth" class="hash-link" aria-label="Direct link to Seeking discomfort for real growth" title="Direct link to Seeking discomfort for real growth" translate="no">​</a></h3>
<p>Recently, I've started experimenting with intentional discomfort as a pathway to personal development not the superficial kind that feels good in the moment but the challenging practices that initially make you question your sanity:</p>
<ul>
<li class=""><strong>Cold showers</strong> instead of hot ones, when every cell in my body protests the shock of cold water against warm skin.</li>
<li class=""><strong>Intermittent fasting</strong> instead of always indulging hunger pangs when they first arise, teaching myself the difference between actual hunger and habitual eating patterns.</li>
<li class=""><strong>Waking up at 5 AM</strong> instead of rolling out of bed last minute, even on weekends when the temptation to burrow deeper under the covers is nearly overwhelming.</li>
</ul>
<p>It wasn't easy at first. My body and mind resisted fiercely complaining, bargaining, and sometimes outright rebelling against these self-imposed challenges. But here's what I discovered through persistence and pushing past that initial wall of resistance:</p>
<p>Discomfort breeds resilience. The more I leaned into these daily challenges, the stronger I felt mentally and physically. Even when my company mandated four days in the office instead of remote work, I reshaped my routine, adjusting my commute, meal prep, and exercise schedule and now, I actually feel better for having adapted rather than complained.</p>
<p>Effort is the price of growth. Nothing worthwhile comes without struggle, and I'm learning to embrace that fundamental truth every single day.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="we-need-struggle-not-for-someone-elses-approval-but-for-ourselves">We need struggle, not for someone else’s approval, but for ourselves.<a href="https://www.agas.dev/blog/seek-discomfort#we-need-struggle-not-for-someone-elses-approval-but-for-ourselves" class="hash-link" aria-label="Direct link to We need struggle, not for someone else’s approval, but for ourselves." title="Direct link to We need struggle, not for someone else’s approval, but for ourselves." translate="no">​</a></h3>
<p>Struggle is not something to be avoided but embraced, not for external validation or fleeting rewards, but for personal growth and fulfillment. In a world increasingly optimized for convenience, where effort is becoming optional and friction is systematically removed, we risk losing something essential which are our resilience, sense of purpose, and capacity for joy.</p>
<p>True satisfaction doesn’t come from comfort alone but from overcoming meaningful challenges and pushing beyond our limits. Instead of chasing easy wins or external markers of success, we should intentionally seek discomfort, whether through physical discipline, mental resilience, or personal transformation. It is through struggle, not avoidance, that we grow into stronger, more fulfilled versions of ourselves.</p>
<p><strong>Don’t grind for money, promotions, or prestige. Those highs fade fast.</strong></p>
<p><strong>Don’t chase easy wins . They’ll leave you emptier than before.</strong></p>
<p>Instead, lean into the process. Pursue challenges that make you a better version of yourself.</p>
<p>For me, that means training for a marathon, reading daily, and chasing personal transformation. For you, it might mean pursuing a dream job in Japan, deepening relationships, or facing fears.</p>
<p>Whatever it is embrace the discomfort.</p>]]></content:encoded>
            <category>personal</category>
            <category>growth</category>
        </item>
        <item>
            <title><![CDATA[Middle Management Is Hard]]></title>
            <link>https://www.agas.dev/blog/middle-management-is-hard</link>
            <guid>https://www.agas.dev/blog/middle-management-is-hard</guid>
            <pubDate>Fri, 01 Nov 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[The challenges of middle management and how leaders can address them. Explore strategies to empower middle managers, reduce burnout, and enhance their role in balancing leadership's vision with organizational outcomes.]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" src="https://oslo.shizu.id/cc1c7482-3f7a-43fd-8737-870f0dcde7a1.jpeg" alt="Middle Management Is Hard" class="img_ev3q"></p>
<!-- -->
<div style="max-width:230px;width:640px;height:50px"></div>
<br>
<p>Lately, I’ve been reflecting a lot on the challenges of middle management, particularly after a conversation with a former coworker. Even though this individual isn’t a middle manager, their insights about feeling constant pressure from leadership really got me thinking. They described how leadership consistently pushes for higher performance and demands results, often without understanding the realities on the ground. It struck me that, while they bear the weight of these demands from leadership, middle managers face a similar pressure but from multiple directions.</p>
<p>Being middle management is incredibly challenging. You are expected to balance pressure from all directions above, below, and even from the sides. My experience comes from the tech industry, but I believe these observations resonate across multiple sectors. Middle managers are the glue that holds organizations together, but they are often underappreciated and left to face unique struggles. Sandwiched between company leadership and employees, they must navigate conflicting demands, limited authority, and, at times, an overwhelming sense of isolation.</p>
<!-- -->
<p>In middle management, you constantly find yourself wedged between the high expectations of leadership and the needs of your subordinates. You're tasked with executing strategies from top management while simultaneously motivating and managing your team to deliver results. At times, you may agree with your team’s frustrations and concerns, yet you're still forced to put on a smile and enforce decisions that come from above, even when you didn’t have a hand in making them. It’s not uncommon for middle managers to find out about tough decisions such as budget cuts, reassignments, or new initiatives at the last moment, just like the rest of their team. This disconnection fuels a feeling of being left out or even powerless, yet you’re still tasked with being accountable for the success of your team.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-many-pressures-facing-middle-management">The Many Pressures Facing Middle Management<a href="https://www.agas.dev/blog/middle-management-is-hard#the-many-pressures-facing-middle-management" class="hash-link" aria-label="Direct link to The Many Pressures Facing Middle Management" title="Direct link to The Many Pressures Facing Middle Management" translate="no">​</a></h3>
<p>Middle managers face a unique set of challenges that stem from being positioned between upper management and front-line teams. This intermediary space creates a range of pressures that require constant navigation.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-top-down-pressure">1. <strong>Top-Down Pressure:</strong><a href="https://www.agas.dev/blog/middle-management-is-hard#1-top-down-pressure" class="hash-link" aria-label="Direct link to 1-top-down-pressure" title="Direct link to 1-top-down-pressure" translate="no">​</a></h4>
<p>At the core of middle management's struggles is top-down pressure. Leadership has high expectations for performance, efficiency, and goal achievement. Strategic decisions are made at the higher levels, but middle managers are often left in the dark, tasked with implementing orders without being part of the decision-making process. For example, Jack Zenger and Joseph Folkman, in their studies of employee engagement, found that middle managers, particularly those with five to ten years of experience, are often disillusioned because they feel undervalued. They’re given tasks to execute but have little say in policy-making, causing frustration and disengagement. Many employees under middle managers don’t realize that these managers are frequently just as frustrated with the decisions from the top.</p>
<p>Middle managers, being closest to the workplace's actual dynamics, often have the most practical knowledge of what will and won’t work. However, they frequently find themselves informed about decisions after the fact and left to carry them out without the support or resources necessary for success. They are, in essence, expected to deliver results from policies in which they had no say.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-bottom-up-frustrations">2. <strong>Bottom-Up Frustrations:</strong><a href="https://www.agas.dev/blog/middle-management-is-hard#2-bottom-up-frustrations" class="hash-link" aria-label="Direct link to 2-bottom-up-frustrations" title="Direct link to 2-bottom-up-frustrations" translate="no">​</a></h4>
<p>On the flip side, middle managers bear the brunt of employee frustrations. For example, in the study by Zenger and Folkman, middle managers are often seen as the “face of management,” and when things go wrong or decisions don’t make sense, employees direct their complaints at these middle-tier leaders. Team members may not be aware that many of their objections are shared by their manager. As Anicich and Hirsh describe in their research on role-switching, middle managers are stuck in a “vertical code-switching” dilemma, where they must alternate between being an authoritative leader and a deferential subordinate, often within the same day.</p>
<p>This constant shifting takes a toll on mental and emotional well-being, leading to high levels of stress and emotional burnout. Middle managers are expected to maintain their team’s morale and motivation, even when upper management decisions leave them just as frustrated and powerless. They absorb complaints from below and directives from above with very little control of their own.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-side-to-side-politics">3. <strong>Side-to-Side Politics:</strong><a href="https://www.agas.dev/blog/middle-management-is-hard#3-side-to-side-politics" class="hash-link" aria-label="Direct link to 3-side-to-side-politics" title="Direct link to 3-side-to-side-politics" translate="no">​</a></h4>
<p>Middle managers also face significant peer pressure. Departments within organizations often operate in silos, and middle managers must compete with their peers for resources, recognition, and strategic visibility. Anicich and Hirsh emphasize that middle managers experience high levels of stress not only from above and below but also from their same-level colleagues. Whether it's negotiating resources, competing for development opportunities, or even juggling inter-departmental expectations, the middle manager experiences ongoing tension with little relief.</p>
<p>In highly competitive corporate environments, especially fast-paced industries like tech, collaboration often turns into a tug-of-war among middle managers, each vying for more bandwidth, budget, or visibility with leadership. If one department doesn’t deliver, the middle manager will feel the pressure from their leadership, even when the root cause of the issue is entirely external.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-emotional-labor-involved">The Emotional Labor Involved<a href="https://www.agas.dev/blog/middle-management-is-hard#the-emotional-labor-involved" class="hash-link" aria-label="Direct link to The Emotional Labor Involved" title="Direct link to The Emotional Labor Involved" translate="no">​</a></h3>
<p>One of the most unseen aspects of middle management is its emotional toll. Julie Zhuo, who became a manager at Facebook (now Meta) at the young age of 25, recalls her early experience feeling like she was "in over her head." Zhuo reflects on the constant emotional labor required to support her subordinates while projecting confidence to her higherups. Like many middle managers, Zhuo initially believed that leadership meant projecting unwavering authority, but she found that approach ultimately alienating. Over time, she realized that successful management was much more about fostering collaboration than rigid top-down decision-making.</p>
<p>Middle managers often feel emotionally drained by the tension of mediating between the frustrations of their team and the demands of their leadership. Eric Anicich and Jacob B. Hirsh argue that middle managers experience high levels of "role conflict," where they're often caught between incompatible expectations. They must be empathetic to their subordinates while projecting confidence and competence to their superiors. This emotional balancing act places middle managers at a higher risk for burnout and mental health issues, including anxiety and depression, compared to employees at either end of the hierarchy.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-isolation-of-middle-management">The Isolation of Middle Management<a href="https://www.agas.dev/blog/middle-management-is-hard#the-isolation-of-middle-management" class="hash-link" aria-label="Direct link to The Isolation of Middle Management" title="Direct link to The Isolation of Middle Management" translate="no">​</a></h3>
<p>Perhaps the most pervasive struggle faced by middle managers is the feeling of isolation. Both Zhuo and Zenger highlight how middle managers are often excluded from key strategic discussions. Many decisions are made at the executive leadership level, leaving middle managers responsible for enforcing and explaining these decisions, often without fully understanding them or having the opportunity to weigh in beforehand. The result is an overwhelming feeling of disconnect. You're expected to deliver results, but you’re not always given the tools or knowledge necessary to do so.</p>
<p>A study cited by Anicich and Hirsh involving over 21,000 employees revealed that middle managers reported higher rates of anxiety, stress, and burnout than those in lower or upper-level positions. This is compounded by the fact that middle managers lack the same support networks that senior executives or frontline employees might have available. Zhuo advises that in her own experience, middle managers need more access to leadership development and strategic involvement to feel less isolated and more connected to the larger mission.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-can-we-do-to-improve-middle-management">What Can We Do to Improve Middle Management?<a href="https://www.agas.dev/blog/middle-management-is-hard#what-can-we-do-to-improve-middle-management" class="hash-link" aria-label="Direct link to What Can We Do to Improve Middle Management?" title="Direct link to What Can We Do to Improve Middle Management?" translate="no">​</a></h3>
<p>In my view, the traditional approach to managing middle managers just isn't cutting it anymore. The ongoing pressures and emotional strain that come from being sandwiched between leadership and teams need to be addressed. Here’s what I think can be done to alleviate some of these challenges.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-bring-middle-managers-into-strategic-conversations-early">1. <strong>Bring Middle Managers into Strategic Conversations Early</strong><a href="https://www.agas.dev/blog/middle-management-is-hard#1-bring-middle-managers-into-strategic-conversations-early" class="hash-link" aria-label="Direct link to 1-bring-middle-managers-into-strategic-conversations-early" title="Direct link to 1-bring-middle-managers-into-strategic-conversations-early" translate="no">​</a></h4>
<p>From what I’ve seen, one of the primary reasons middle managers feel disengaged is simply because they’re left out of major strategic decisions. As Jack Zenger and Joseph Folkman point out, this disconnection makes their job ten times harder. If leadership wants to get the best out of middle managers, they need to be brought into the conversation from the start. Whether it’s helping to shape company policies or providing feedback on new initiatives, direction will stick far better if middle managers understand and feel a part of the “why” behind these decisions. Giving them this sense of ownership fosters better implementation and ensures strategic objectives are grounded in the realities of the workplace.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-recognize-and-reward-the-emotional-labor-of-middle-management">2. <strong>Recognize and Reward the Emotional Labor of Middle Management</strong><a href="https://www.agas.dev/blog/middle-management-is-hard#2-recognize-and-reward-the-emotional-labor-of-middle-management" class="hash-link" aria-label="Direct link to 2-recognize-and-reward-the-emotional-labor-of-middle-management" title="Direct link to 2-recognize-and-reward-the-emotional-labor-of-middle-management" translate="no">​</a></h4>
<p>Let’s face it, emotional labor is a huge part of a middle manager’s role and often goes unrecognized. These managers aren’t just managing tasks and project outcomes, they’re balancing the emotional well-being of their teams while meeting deadlines.</p>
<p>But unfortunately, this aspect of the job is often perceived as transactional something that just comes with the territory. This is where companies need to step up. Recognize the complexity of the role. We need to do more than just acknowledge it, we need to actively reward it. And by reward, I don’t mean just compensation, but through recognition, support systems, and creating work cultures that reinforce the value they bring. Providing mental health resources is also crucial because it helps middle managers cope with the stress and ensures their emotional well-being, which in turn benefits the entire team.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://www.agas.dev/blog/middle-management-is-hard#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h3>
<p>Middle management is often one of the toughest, most thankless jobs in any organization. These individuals are tasked with balancing competing expectations, processing complex emotions, and producing excellent results despite limited authority and frequent isolation. However, the good news is that these challenges are not set in stone they can be managed and improved upon.</p>
<p>To make middle management more effective and sustainable, we need to start by rethinking the role. Empowering them with decision-making autonomy, involving them earlier in strategic discussions, prioritizing their own leadership development, building supportive peer networks, and recognizing the weight of their emotional labor are all essential steps. While these changes require deliberate effort from leadership, they are essential if organizations want to get the most out of their middle-tier management.</p>
<p>As Jack Zenger and Joseph Folkman have pointed out, middle managers aren’t disengaged because they lack ability they are disengaged because they aren’t managed well. The sooner organizations realize the centrality and importance of middle management, the sooner they can align themselves for sustainable success. After all, when middle managers thrive, so does the entire organization from the frontlines to the C-level.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="recommended-reading-materials">Recommended Reading Materials<a href="https://www.agas.dev/blog/middle-management-is-hard#recommended-reading-materials" class="hash-link" aria-label="Direct link to Recommended Reading Materials" title="Direct link to Recommended Reading Materials" translate="no">​</a></h3>
<p>While making this article, I consulted multiple reading materials and papers to gain deeper insights into the challenges of middle management and strategies to overcome them. These resources provided me with useful frameworks and data that helped shape my understanding of this complex role.</p>
<ol>
<li class=""><a href="https://hbr.org/podcast/2024/01/how-to-become-a-better-manager" target="_blank" rel="noopener noreferrer" class="">How to Become a Better Manager</a></li>
<li class=""><a href="https://hbr.org/2017/03/why-being-a-middle-manager-is-so-exhausting" target="_blank" rel="noopener noreferrer" class="">Why Being a Middle Manager Is So Exhausting</a></li>
<li class=""><a href="https://hbr.org/2014/11/why-middle-managers-are-so-unhappy" target="_blank" rel="noopener noreferrer" class="">Why Middle Managers Are So Unhappy</a></li>
<li class=""><a href="https://hbr.org/2024/02/a-guide-for-new-middle-managers" target="_blank" rel="noopener noreferrer" class="">A Guide for New Middle Managers</a></li>
<li class=""><a href="https://www.researchgate.net/publication/313739114_The_Psychology_of_Middle_Power_Vertical_Code-Switching_Role_Conflict_and_Behavioral_Inhibition" target="_blank" rel="noopener noreferrer" class="">The Psychology of Middle Power: Vertical Code-Switching, Role Conflict, and Behavioral Inhibition</a></li>
</ol>]]></content:encoded>
            <category>middlemanagement</category>
            <category>leadership</category>
        </item>
        <item>
            <title><![CDATA[The AI Future of Frontend Development]]></title>
            <link>https://www.agas.dev/blog/the-ai-future-of-frontend</link>
            <guid>https://www.agas.dev/blog/the-ai-future-of-frontend</guid>
            <pubDate>Wed, 22 Nov 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[The impact of AI on frontend development and the evolving role of frontend engineers. Discover how GPT-4 and AI technologies are revolutionizing web design and development.]]></description>
            <content:encoded><![CDATA[<p>In the rapidly evolving landscape of technology, the fusion of AI with frontend development heralds a transformative era. With GPT-4's groundbreaking capabilities, the process of creating websites is poised for a revolutionary shift. Imagine this: you snap a picture of a design, and GPT-4 translates it into functional code. This vision is not just a fantasy; it's swiftly becoming a reality.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-role-of-frontend-engineers-in-the-ai-era">The Role of Frontend Engineers in the AI Era<a href="https://www.agas.dev/blog/the-ai-future-of-frontend#the-role-of-frontend-engineers-in-the-ai-era" class="hash-link" aria-label="Direct link to The Role of Frontend Engineers in the AI Era" title="Direct link to The Role of Frontend Engineers in the AI Era" translate="no">​</a></h2>
<p>The integration of AI into the realm of frontend development, especially with the advent of technologies like GPT-4, is reshaping the landscape of the field. This change prompts a crucial inquiry: what will be the role of frontend engineers in the next decade? It's easy to jump to conclusions and view the rise of AI as a threat to traditional roles. However, a more nuanced perspective reveals that this is not about the end of frontend engineering, but rather an evolution. As AI takes over more routine, templated tasks, frontend engineers are presented with the opportunity to shift their focus. Their role will likely transform, emphasizing more on creative problem-solving, strategic planning, and complex task management that AI cannot easily replicate.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-transformative-impact-of-ai-on-frontend-engineering">The Transformative Impact of AI on Frontend Engineering<a href="https://www.agas.dev/blog/the-ai-future-of-frontend#the-transformative-impact-of-ai-on-frontend-engineering" class="hash-link" aria-label="Direct link to The Transformative Impact of AI on Frontend Engineering" title="Direct link to The Transformative Impact of AI on Frontend Engineering" translate="no">​</a></h3>
<p>In the AI-augmented future, frontend engineers will find their roles becoming more integral to the creative and strategic aspects of web development. AI tools, like GPT-4, will handle the coding of basic layouts and structures, allowing engineers to concentrate on innovative design solutions, user experience enhancements, and intricate technical challenges. This shift will demand a broader skill set from frontend engineers, including a deeper understanding of AI capabilities and limitations. Their expertise will be crucial in guiding AI tools to achieve desired outcomes, blending technical proficiency with creative vision. As AI handles more of the repetitive work, frontend engineers can explore new technologies, experiment with emerging design trends, and push the boundaries of what's possible in web development.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-collaborative-future">A Collaborative Future<a href="https://www.agas.dev/blog/the-ai-future-of-frontend#a-collaborative-future" class="hash-link" aria-label="Direct link to A Collaborative Future" title="Direct link to A Collaborative Future" translate="no">​</a></h3>
<p>Looking ahead, the collaboration between frontend engineers and AI is set to redefine the industry. This partnership will not only make the development process more efficient but also open doors to unprecedented levels of creativity and innovation. Frontend engineers will play a pivotal role in steering this collaboration, leveraging AI to optimize workflows and implement cutting-edge designs. Their role will evolve to be more strategic and creative, focusing on areas where human insight is irreplaceable. In essence, the future of frontend engineering in the AI era is not about replacement but augmentation, where human ingenuity and AI efficiency come together to create a more dynamic, innovative, and exciting field of web development.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="balancing-the-excitement-and-apprehensions-of-ai-advancements">Balancing the Excitement and Apprehensions of AI Advancements<a href="https://www.agas.dev/blog/the-ai-future-of-frontend#balancing-the-excitement-and-apprehensions-of-ai-advancements" class="hash-link" aria-label="Direct link to Balancing the Excitement and Apprehensions of AI Advancements" title="Direct link to Balancing the Excitement and Apprehensions of AI Advancements" translate="no">​</a></h3>
<p>The pace at which AI is advancing in frontend development is undeniably exhilarating. This rapid progression brings with it a host of new tools and methodologies that promise to revolutionize the way websites and applications are designed and developed. As AI technologies become more sophisticated, they offer frontend developers the ability to automate mundane tasks, experiment with innovative design concepts, and optimize user experiences in ways that were previously impossible. This surge of innovation opens up a world of possibilities, allowing for more creative freedom and efficiency in the development process. The excitement around these advancements stems from the potential they hold to elevate the field to new heights, offering developers the tools to push the boundaries of what's technically and creatively feasible.</p>
<p>However, with this rapid evolution comes a certain level of apprehension. The introduction of AI in frontend development disrupts established practices, challenging the traditional roles and workflows of professionals in the field. <strong>Such disruption can be daunting, as it requires developers to adapt to new tools and rethink their approach to design and development</strong>. But rather than viewing this as a threat, it's more productive to see it as an opportunity for growth and learning. This period of transition provides a unique chance for frontend developers to expand their skill set, embrace new technologies, and position themselves at the forefront of the industry. By adapting to these changes and learning to harness the power of AI, frontend professionals can not only secure their place in the future of web development but also contribute to shaping that future in innovative and exciting ways.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="boosting-productivity-with-ai-a-new-frontier-for-frontend-engineers">Boosting Productivity with AI: A New Frontier for Frontend Engineers<a href="https://www.agas.dev/blog/the-ai-future-of-frontend#boosting-productivity-with-ai-a-new-frontier-for-frontend-engineers" class="hash-link" aria-label="Direct link to Boosting Productivity with AI: A New Frontier for Frontend Engineers" title="Direct link to Boosting Productivity with AI: A New Frontier for Frontend Engineers" translate="no">​</a></h3>
<p>The integration of AI in frontend development is not about replacing engineers but enhancing their capabilities. With AI's assistance, frontend engineers can significantly boost their productivity. Tasks like quick prototyping and deploying landing pages could be accomplished with minimal intervention, allowing engineers to focus on more complex and creative aspects of development.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ai-as-a-catalyst-for-innovation-at-ruangguru">AI as a Catalyst for Innovation at Ruangguru<a href="https://www.agas.dev/blog/the-ai-future-of-frontend#ai-as-a-catalyst-for-innovation-at-ruangguru" class="hash-link" aria-label="Direct link to AI as a Catalyst for Innovation at Ruangguru" title="Direct link to AI as a Catalyst for Innovation at Ruangguru" translate="no">​</a></h2>
<p>At Ruangguru, we are particularly excited about harnessing AI to redefine frontend development. Our vision is to leverage AI not just as a tool but as a partner in innovation. By integrating AI into our design and product teams, we can expedite the prototyping process, deploy landing pages swiftly, and achieve a level of efficiency previously unimaginable. This is the innovation we are eager to bring to the forefront – a synergy of AI and human creativity that pushes the boundaries of what's possible in frontend development.</p>]]></content:encoded>
            <category>frontend</category>
            <category>ai</category>
        </item>
        <item>
            <title><![CDATA[The Beauty Of Apple Airplay]]></title>
            <link>https://www.agas.dev/blog/the-beauty-of-airplay</link>
            <guid>https://www.agas.dev/blog/the-beauty-of-airplay</guid>
            <pubDate>Fri, 17 Nov 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Explore Apple's AirPlay for multi-room audio streaming. Learn how AirPlay 2 with iOS 17 features enhances audio quality for a superior home setup.]]></description>
            <content:encoded><![CDATA[<p>Once upon a time, in the land of smartphones and gadgets, I was a loyal subject of the Kingdom of Android, reveling in its open plains and diverse landscapes. But as my wallet grew a bit thicker (thanks to some good fortune and maybe skipping a few fancy coffees), I crossed the border into the sleek dominion of Apple. Here, I discovered a world where everything 'just works' - often with a price tag that makes you think it should also cook dinner and do the laundry! Despite the premium cost, I was hooked on the harmony and simplicity Apple products offered, a stark contrast to my former Android allegiance.</p>
<p>The real game-changer, however, was my encounter with AirPlay 2. This feature epitomizes why I've become enamored with Apple's offerings. I have an array of speakers in my home, including multiple HomePods and the Samsung Q700A, all AirPlay-enabled. AirPlay 2's multi-speaker functionality has transformed my home into a symphony of sound. The ease with which I can fill every corner of my house with music is nothing short of enchanting. It's not just the technology itself; it's how it enhances the atmosphere of my home, making it more welcoming and lively.</p>
<p>But there's more magic in the mix, especially with the latest iOS 17 features. When friends visit, they can effortlessly add tracks to the speaker's queue, enriching our gatherings with their musical tastes. This seamless integration and shared control are what make AirPlay 2 so special to me. It's not just a feature; it's an experience, a testament to the thoughtfulness Apple puts into creating an ecosystem that's not only functional but also brings people together. This is why I've fallen deeply in love with the Apple ecosystem – it's not just technology; it's technology that connects, entertains, and simplifies life in the most delightful ways.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="harmonizing-spaces-with-airplays-multiroom-audio-magic">Harmonizing Spaces with AirPlay's Multiroom Audio Magic<a href="https://www.agas.dev/blog/the-beauty-of-airplay#harmonizing-spaces-with-airplays-multiroom-audio-magic" class="hash-link" aria-label="Direct link to Harmonizing Spaces with AirPlay's Multiroom Audio Magic" title="Direct link to Harmonizing Spaces with AirPlay's Multiroom Audio Magic" translate="no">​</a></h2>
<p>The wonder of multiroom audio, particularly through AirPlay, has transformed my home into a vibrant, melodious realm, limited yet enchanting in its reach. In my space, the magic unfolds specifically within the confines of my office and kitchen. Imagine classical tunes gently wafting through my office as I work, while upbeat pop rhythms pulse in the kitchen. This dual-zone symphony, seamlessly managed via AirPlay, creates a delightful juxtaposition of moods, catering to the different atmospheres of each room. The ability to play a podcast in the kitchen, keeping me entertained as I cook, and then effortlessly switch to tranquil instrumental music in my office is more than just a convenience – it's a daily luxury.</p>
<p>The charm of this setup lies in its incredibly intuitive control. AirPlay allows me to effortlessly orchestrate the audio experiences of these two spaces with just a few taps on my Apple device. Whether I'm syncing the two rooms to fill my home with a uniform melody or curating distinct soundtracks for each, the process is remarkably straightforward. This seamless control makes the system feel like an extension of my own senses, responding fluidly to my commands. Although my setup is limited to the office and kitchen, it never feels restrictive. Instead, it offers a perfect blend of functionality and enjoyment, showcasing how AirPlay's multiroom audio capability can transform even a segment of a home into a harmonious, melodious sanctuary.</p>
<p>Additionally, the multiroom audio feature through AirPlay isn’t just about playing different tunes in separate rooms; it’s equally adept at unifying the audio experience across spaces. On occasions when the mood strikes, I can have the same melodious song or an engaging podcast playing simultaneously in both my office and kitchen. This unified auditory experience creates a sense of cohesion and continuity throughout my home, making the transition from one room to another a seamless journey through the same sonic landscape. It’s almost like the music is following me, providing a consistent backdrop to my daily activities, whether I’m deep in work or bustling about in the kitchen. This ability to synchronize audio across rooms with such ease is a testament to the sophisticated yet user-friendly design of AirPlay’s multiroom capabilities.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="transform-your-gathering-with-ios-17-shared-playlists-and-whole-home-sound">Transform Your Gathering with iOS 17 Shared Playlists and Whole-Home Sound<a href="https://www.agas.dev/blog/the-beauty-of-airplay#transform-your-gathering-with-ios-17-shared-playlists-and-whole-home-sound" class="hash-link" aria-label="Direct link to Transform Your Gathering with iOS 17 Shared Playlists and Whole-Home Sound" title="Direct link to Transform Your Gathering with iOS 17 Shared Playlists and Whole-Home Sound" translate="no">​</a></h2>
<p>The introduction of iOS 17 has brought a delightful twist to the way music animates gatherings in my home. Now, anyone with an iPhone and a taste for tunes can contribute to the musical atmosphere. This shared music feature, in harmony with AirPlay's multiroom audio capability, has essentially transformed my living space into a dynamic, crowd-sourced jukebox.</p>
<p>Imagine this: friends walking in, iPhones in hand, each eager to share their favorite tracks. With a few simple taps, they add their chosen songs to the mix. This isn’t just about playing music; it’s about creating a collective musical journey, where each person's preferences contribute to the evening’s soundtrack. The beauty lies in the spontaneity and diversity of the playlist that evolves as the night progresses.</p>
<p>Coupled with the multiroom audio setup, this feature elevates the party experience to new heights. As each new song queues up, it doesn't just play in one room; it resonates throughout the entire house. Whether you're in the throes of a lively conversation in the living room or grabbing a snack in the kitchen, the music follows, unifying the party across different spaces. It's like having a personal DJ who understands the vibe of the room and tailors the music to fit the mood, except the DJ is a collective of your friends' musical tastes.</p>
<p>This isn't just a technological advancement; it's a social one. iOS 17's shared music feature, combined with AirPlay's multiroom audio, doesn't just change how we listen to music at parties; it changes how we interact, share, and enjoy moments together. It’s a testament to how technology can not only connect devices but also people, making every gathering a unique and memorable experience.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="unveiling-the-technical-mastery-of-airplay-2">Unveiling the Technical Mastery of AirPlay 2<a href="https://www.agas.dev/blog/the-beauty-of-airplay#unveiling-the-technical-mastery-of-airplay-2" class="hash-link" aria-label="Direct link to Unveiling the Technical Mastery of AirPlay 2" title="Direct link to Unveiling the Technical Mastery of AirPlay 2" translate="no">​</a></h2>
<p>AirPlay 2, unveiled by Apple in 2017 and enhanced for multi-room audio capabilities with iOS 11.4, stands as a testament to modern streaming technology. This sophisticated protocol suite is primarily designed for streaming audio, but it also supports video and photos, showcasing its versatility. At its core, AirPlay 2 relies on Apple's Bonjour technology for service discovery, employing multicast DNS (mDNS) and the zeroconf protocol. This allows AirPlay devices to discover each other seamlessly over a shared network, paving the way for effortless connection and synchronization.</p>
<p>The transmission of audio data in AirPlay 2 utilizes the Real-Time Protocol (RTP), a foundation that ensures reliable delivery of streaming content. The RTP packets, carrying the audio data, are encrypted using the ChaCha20-Poly1305 AEAD algorithm, a choice reflecting Apple's commitment to security. During the streaming setup, the sender shares the encryption key with the receiver through a SETUP RTSP request, establishing a secure channel for audio transmission. This encryption mechanism is vital, not just for real-time audio streaming used by platforms like Spotify and YouTube, but also for buffered audio streams, which are typical in Apple Music, where each RTP packet is methodically prefixed with its size.</p>
<p>Authentication in AirPlay 2 is layered and versatile, offering different methods based on the receiver's capabilities. The system primarily supports MFi (Made for iPhone/iPod/iPad) authentication and FairPlay authentication, with the older RSA method now being phased out. This approach to authentication ensures that only authorized devices can access the streaming content, adding an extra layer of security to the user's experience. In scenarios where authentication bits are not set, AirPlay 2 defaults to an error state, underscoring the necessity of authentication in maintaining a secure streaming environment.</p>
<p>AirPlay 2's approach to audio streaming is not just about secure transmission but also about offering an enriched user experience. The technology supports both real-time and buffered audio streaming, catering to different use cases and preferences. Real-time streaming is favored for immediate playback, while buffered streaming, where data is sent in advance, allows for a more stable and uninterrupted listening experience, especially beneficial for high-quality audio content. This flexibility in streaming methods ensures that AirPlay 2 can provide a smooth and high-quality audio experience regardless of the network conditions or the type of content being played.</p>
<p>In conclusion, AirPlay 2 is a remarkable blend of advanced streaming protocols, robust security measures, and user-centric features. Its ability to seamlessly integrate into the Apple ecosystem, while offering a secure, versatile, and high-quality streaming experience, marks it as a pivotal technology in the realm of digital media. Whether it's for multi-room audio setups or streaming your favorite show, AirPlay 2's technology ensures that your media is not only easily accessible but also delivered with exceptional fidelity and security.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="airplay-2-and-the-pinnacle-of-audio-quality">AirPlay 2 and the Pinnacle of Audio Quality<a href="https://www.agas.dev/blog/the-beauty-of-airplay#airplay-2-and-the-pinnacle-of-audio-quality" class="hash-link" aria-label="Direct link to AirPlay 2 and the Pinnacle of Audio Quality" title="Direct link to AirPlay 2 and the Pinnacle of Audio Quality" translate="no">​</a></h2>
<p>AirPlay 2 elevates the standard of audio streaming, boasting an impressive array of supported audio formats that cater to high-fidelity sound enthusiasts. The technology embraces a comprehensive range of formats, from the standard PCM to the more sophisticated ALAC (Apple Lossless Audio Codec), ensuring that every nuance and detail of the original recording is preserved. This range includes PCM formats with sample rates from 8000 Hz to 48000 Hz and bit depths of 16 or 24 bits, offering versatility for different audio qualities. Additionally, support for ALAC at both 44100 Hz and 48000 Hz in 16 and 24 bits ensures that lossless audio can be transmitted without any compromise in quality. This extensive support of audio formats underlines Apple’s commitment to delivering a superior auditory experience, making AirPlay 2 an ideal choice for audiophiles and casual listeners alike.</p>
<p>Moreover, AirPlay 2's capability extends to accommodate various AAC (Advanced Audio Coding) formats, including AAC-LC and AAC-ELD, which are known for their efficiency in streaming high-quality audio at lower bit rates. This makes AirPlay 2 adaptable for different network conditions, ensuring a consistent delivery of high-quality sound even in less than optimal streaming environments. The inclusion of the OPUS format, particularly beneficial for voice transmissions, further diversifies AirPlay 2's audio format repertoire. This meticulous attention to supporting a wide range of audio formats demonstrates AirPlay 2's dedication to delivering an exceptional listening experience, where every beat, note, and tone is conveyed with the utmost clarity and richness.</p>]]></content:encoded>
            <category>AirPlay 2</category>
            <category>Multi-Room Audio</category>
            <category>iOS 17</category>
            <category>High-Fidelity Sound</category>
            <category>HomePod</category>
            <category>Apple Ecosystem</category>
            <category>Wireless Audio</category>
            <category>Streaming Technology</category>
            <category>Audio Encryption</category>
            <category>Home Entertainment</category>
        </item>
        <item>
            <title><![CDATA[GitHub Actions for Easy ARM64 and AMD64 Docker Image Builds]]></title>
            <link>https://www.agas.dev/blog/github-actions-docker-build</link>
            <guid>https://www.agas.dev/blog/github-actions-docker-build</guid>
            <pubDate>Thu, 16 Nov 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Learn to build multi-architecture Docker images for ARM64 and AMD64 using GitHub Actions, with a focus on QEMU, Docker Buildx, caching, and security.]]></description>
            <content:encoded><![CDATA[<p>In today's rapidly evolving software development landscape, the ability to efficiently build and deploy applications across diverse architectures is crucial. This article will explore an advanced yet accessible approach to creating multi-architecture Docker images, specifically for ARM64 and AMD64 platforms, using GitHub Actions. We will dissect a YAML configuration for a GitHub Actions workflow named 'Deploy Production,' illustrating how to automate the building and pushing of Docker images to Docker Hub.</p>
<p>Our focus will be on leveraging the capabilities of GitHub Actions, including setup of QEMU for emulation and Docker Buildx for building images, along with caching strategies and security practices for Docker Hub integration. This guide aims to equip developers with the knowledge and tools needed to streamline their CI/CD pipelines, ensuring seamless deployment across varied computing environments.</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> Deploy Production</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token key atrule">on</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token key atrule">workflow_dispatch</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token key atrule">jobs</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token key atrule">build-and-push-docker</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token key atrule">runs-on</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> ubuntu</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">latest</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token key atrule">steps</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> actions/checkout@v2</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> Set up QEMU</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> docker/setup</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">qemu</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">action@v1</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> Set up Docker Buildx</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> docker/setup</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">buildx</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">action@v1</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> Cache Docker layers</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> actions/cache@v2</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">          </span><span class="token key atrule">path</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> /tmp/.buildx</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">cache</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">          </span><span class="token key atrule">key</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> runner.os </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">buildx</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">$</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> github.sha </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">          </span><span class="token key atrule">restore-keys</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">|</span><span class="token scalar string" style="color:rgb(195, 232, 141)"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token scalar string" style="color:rgb(195, 232, 141)">            ${{ runner.os }}-buildx-</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> Login to Docker Hub</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> docker/login</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">action@v1</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">          </span><span class="token key atrule">username</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> secrets.DOCKERHUB_USERNAME </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">          </span><span class="token key atrule">password</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> secrets.DOCKERHUB_TOKEN </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> Build and push</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> docker/build</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">push</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">action@v2</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">          </span><span class="token key atrule">context</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> .</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">          </span><span class="token key atrule">file</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> ./Dockerfile</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">          </span><span class="token key atrule">push</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token boolean important" style="color:rgb(255, 88, 116)">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">          </span><span class="token key atrule">tags</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> agasdrm/blog</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain">latest</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">agasdrm/blog</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain">$</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> github.run_number </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">          </span><span class="token key atrule">platforms</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> linux/amd64</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">linux/arm64/v8</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">          </span><span class="token key atrule">cache-from</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> type=local</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">src=/tmp/.buildx</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">cache</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">          </span><span class="token key atrule">cache-to</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> type=local</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">dest=/tmp/.buildx</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">cache</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="overview-of-docker-and-github-actions">Overview of Docker and GitHub Actions<a href="https://www.agas.dev/blog/github-actions-docker-build#overview-of-docker-and-github-actions" class="hash-link" aria-label="Direct link to Overview of Docker and GitHub Actions" title="Direct link to Overview of Docker and GitHub Actions" translate="no">​</a></h3>
<p>Docker has revolutionized how we build, share, and run applications by enabling them to run in isolated containers. These containers package up code and all its dependencies, ensuring that the application runs quickly and reliably in various computing environments.</p>
<p>GitHub Actions, on the other hand, is a CI/CD (Continuous Integration/Continuous Delivery) platform that allows you to automate your build, test, and deployment pipeline. It is seamlessly integrated into GitHub, providing an efficient way to apply DevOps practices without leaving the GitHub ecosystem.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-importance-of-multi-architecture-builds">The Importance of Multi-Architecture Builds<a href="https://www.agas.dev/blog/github-actions-docker-build#the-importance-of-multi-architecture-builds" class="hash-link" aria-label="Direct link to The Importance of Multi-Architecture Builds" title="Direct link to The Importance of Multi-Architecture Builds" translate="no">​</a></h3>
<p>With the growing diversity in computing hardware, it's become crucial to ensure that applications can run on different architectures. ARM64 and AMD64 are two prevalent architectures in use today. ARM64 is commonly found in mobile devices, newer desktops, and servers, while AMD64 (x86_64) is widely used in traditional laptops and desktops. Supporting both architectures ensures that your application can reach a wider audience and function in a broader range of environments.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="prerequisites">Prerequisites<a href="https://www.agas.dev/blog/github-actions-docker-build#prerequisites" class="hash-link" aria-label="Direct link to Prerequisites" title="Direct link to Prerequisites" translate="no">​</a></h2>
<p>Before diving into the process, ensure you have the following:</p>
<ul>
<li class="">A <strong>GitHub account</strong> - for accessing GitHub and using GitHub Actions.</li>
<li class="">A <strong>Docker Hub account</strong> - for storing and managing your Docker images.</li>
<li class=""><strong>Basic understanding of Docker</strong> - familiarity with Docker concepts like images, containers, and Dockerfiles.</li>
<li class=""><strong>Basic understanding of Git and GitHub</strong> - knowledge of repositories, branches, commits, and pulls.</li>
<li class=""><strong>Access to a code editor</strong> - like Visual Studio Code, Sublime Text, or any editor of your choice.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="understanding-the-dockerfile">Understanding the Dockerfile<a href="https://www.agas.dev/blog/github-actions-docker-build#understanding-the-dockerfile" class="hash-link" aria-label="Direct link to Understanding the Dockerfile" title="Direct link to Understanding the Dockerfile" translate="no">​</a></h2>
<p>A Dockerfile is a text document containing all the commands a user could call on the command line to assemble an image. Using a Dockerfile, Docker can automatically build an image that includes your application and its dependencies.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="key-components-of-a-dockerfile">Key Components of a Dockerfile<a href="https://www.agas.dev/blog/github-actions-docker-build#key-components-of-a-dockerfile" class="hash-link" aria-label="Direct link to Key Components of a Dockerfile" title="Direct link to Key Components of a Dockerfile" translate="no">​</a></h3>
<ul>
<li class=""><strong>FROM</strong>: Specifies the base image from which you are building.</li>
<li class=""><strong>RUN</strong>: Executes any commands in a new layer on top of the current image.</li>
<li class=""><strong>COPY</strong>: Copies files or directories from your project into the Docker container.</li>
<li class=""><strong>CMD</strong>: Provides defaults for executing a container.</li>
<li class=""><strong>EXPOSE</strong>: Informs Docker that the container listens on specific network ports at runtime.</li>
<li class=""><strong>ENV</strong>: Sets environment variables.</li>
</ul>
<p>Each instruction in a Dockerfile creates a layer in the image. When you change the Dockerfile and rebuild the image, only those layers that have changed are rebuilt. This is part of what makes images so lightweight, small, and fast when compared to other virtualization technologies.</p>
<p>In the next section, we will dive deeper into setting up our GitHub Actions workflow to automate the building and publishing of our Docker images for ARM64 and AMD64 architectures.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="understanding-github-actions">Understanding GitHub Actions<a href="https://www.agas.dev/blog/github-actions-docker-build#understanding-github-actions" class="hash-link" aria-label="Direct link to Understanding GitHub Actions" title="Direct link to Understanding GitHub Actions" translate="no">​</a></h2>
<p>GitHub Actions is a powerful automation tool that integrates deeply with GitHub, providing a platform for automating software workflows. It's particularly useful in implementing Continuous Integration (CI) and Continuous Delivery (CD) processes directly within your repository.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-are-github-actions">What are GitHub Actions?<a href="https://www.agas.dev/blog/github-actions-docker-build#what-are-github-actions" class="hash-link" aria-label="Direct link to What are GitHub Actions?" title="Direct link to What are GitHub Actions?" translate="no">​</a></h3>
<ul>
<li class=""><strong>Automated Workflows</strong>: GitHub Actions allows you to create custom software development life cycle (SDLC) workflows directly in your GitHub repository.</li>
<li class=""><strong>Event-Driven</strong>: These workflows can be triggered by a variety of events within GitHub, such as push, pull requests, issue creation, or even a manual trigger.</li>
<li class=""><strong>Customizable</strong>: GitHub Actions provides a wide range of built-in actions, but you can also create and share your own actions or use actions shared by the GitHub community.</li>
<li class=""><strong>Containers and Virtual Environments</strong>: Actions can run in containers or virtual environments that you specify, ensuring consistency across different runs.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="cicd-with-github-actions">CI/CD with GitHub Actions<a href="https://www.agas.dev/blog/github-actions-docker-build#cicd-with-github-actions" class="hash-link" aria-label="Direct link to CI/CD with GitHub Actions" title="Direct link to CI/CD with GitHub Actions" translate="no">​</a></h3>
<p>In Continuous Integration (CI), code changes are automatically tested and merged into a central repository. Continuous Delivery (CD) extends CI by automatically deploying all code changes to a testing or production environment. GitHub Actions facilitates both CI and CD by automating these processes, enhancing software quality and development speed.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="workflow-syntax-explanation">Workflow Syntax Explanation<a href="https://www.agas.dev/blog/github-actions-docker-build#workflow-syntax-explanation" class="hash-link" aria-label="Direct link to Workflow Syntax Explanation" title="Direct link to Workflow Syntax Explanation" translate="no">​</a></h2>
<p>GitHub Actions utilizes workflows, which are defined by a YAML file stored in your repository's <code>.github/workflows</code> directory. These workflows specify the actions to be executed based on defined events.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="understanding-the-workflow_dispatch-trigger">Understanding the <code>workflow_dispatch</code> Trigger<a href="https://www.agas.dev/blog/github-actions-docker-build#understanding-the-workflow_dispatch-trigger" class="hash-link" aria-label="Direct link to understanding-the-workflow_dispatch-trigger" title="Direct link to understanding-the-workflow_dispatch-trigger" translate="no">​</a></h3>
<ul>
<li class=""><strong>Manual Trigger</strong>: The <code>workflow_dispatch</code> event allows you to manually trigger a workflow run. This is useful when you want to have control over when to run the workflow, as opposed to automatic triggers like push or pull requests.</li>
<li class=""><strong>Inputs</strong>: You can define inputs for the <code>workflow_dispatch</code> trigger, allowing you to pass parameters to the workflow at runtime.</li>
</ul>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> Deploy Production</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token key atrule">on</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token key atrule">workflow_dispatch</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token key atrule">inputs</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token key atrule">environment</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token key atrule">description</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'Deployment Environment'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token key atrule">required</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token boolean important" style="color:rgb(255, 88, 116)">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token key atrule">default</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'staging'</span><br></span></code></pre></div></div>
<p>In this example, the workflow_dispatch is configured with an input named environment. When triggering the workflow manually, you can specify the environment to which you want to deploy.</p>
<p>Workflow Anatomy A typical workflow file consists of the following key sections:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> A name for your workflow.</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token key atrule">on</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> The event that triggers the workflow.</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token key atrule">jobs</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> A set of jobs that the workflow will execute.</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token key atrule">steps</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> Individual tasks that run within a job.</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token key atrule">runs-on</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> Specifies the type of machine to run the job on.</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> Refers to an action to execute as part of a step.</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="preparing-for-docker-build">Preparing for Docker Build<a href="https://www.agas.dev/blog/github-actions-docker-build#preparing-for-docker-build" class="hash-link" aria-label="Direct link to Preparing for Docker Build" title="Direct link to Preparing for Docker Build" translate="no">​</a></h2>
<p>Before diving into the actual Docker build process, it's crucial to understand the role of QEMU in this workflow and how it's set up using GitHub Actions.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-is-qemu">What is QEMU?<a href="https://www.agas.dev/blog/github-actions-docker-build#what-is-qemu" class="hash-link" aria-label="Direct link to What is QEMU?" title="Direct link to What is QEMU?" translate="no">​</a></h3>
<p>QEMU (Quick Emulator) is an open-source emulator that performs hardware virtualization. It is a key tool in the process of running software that is built for one type of processor architecture on another. This capability is particularly important when dealing with Docker images intended for multiple architectures.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="role-of-qemu-in-docker-builds">Role of QEMU in Docker Builds<a href="https://www.agas.dev/blog/github-actions-docker-build#role-of-qemu-in-docker-builds" class="hash-link" aria-label="Direct link to Role of QEMU in Docker Builds" title="Direct link to Role of QEMU in Docker Builds" translate="no">​</a></h3>
<ul>
<li class=""><strong>Emulation of Architectures</strong>: QEMU allows us to emulate different processor architectures, such as ARM64, on machines that have a different architecture (like AMD64). This is vital for building Docker images that are meant to run on multiple architectures.</li>
<li class=""><strong>Cross-Platform Compatibility</strong>: By using QEMU, we ensure that the Docker images built are compatible across different architectures, making our application more versatile and accessible.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="setting-up-qemu-in-github-actions">Setting Up QEMU in GitHub Actions<a href="https://www.agas.dev/blog/github-actions-docker-build#setting-up-qemu-in-github-actions" class="hash-link" aria-label="Direct link to Setting Up QEMU in GitHub Actions" title="Direct link to Setting Up QEMU in GitHub Actions" translate="no">​</a></h3>
<p>The <code>docker/setup-qemu-action@v1</code> action is used to set up QEMU in the GitHub Actions runner environment. This action simplifies the process of installing and configuring QEMU for our build process.</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> Set up QEMU</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> docker/setup</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">qemu</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">action@v1</span><br></span></code></pre></div></div>
<p>In the workflow file, this step ensures that QEMU is correctly set up and ready to enable emulation for different architectures during the Docker build process.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="docker-buildx-setup">Docker Buildx Setup<a href="https://www.agas.dev/blog/github-actions-docker-build#docker-buildx-setup" class="hash-link" aria-label="Direct link to Docker Buildx Setup" title="Direct link to Docker Buildx Setup" translate="no">​</a></h2>
<p>Docker Buildx is an extended build feature of Docker that enables the creation of multi-architecture images.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="understanding-docker-buildx">Understanding Docker Buildx<a href="https://www.agas.dev/blog/github-actions-docker-build#understanding-docker-buildx" class="hash-link" aria-label="Direct link to Understanding Docker Buildx" title="Direct link to Understanding Docker Buildx" translate="no">​</a></h3>
<ul>
<li class=""><strong>Extended Features</strong>: Docker Buildx extends the capabilities of the standard Docker build commands, allowing for more advanced features, including building for multiple architectures from a single platform.</li>
<li class=""><strong>Simplifying Multi-Arch Builds</strong>: With Buildx, you can easily build images for architectures like ARM64 and AMD64, even if your build environment is different.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="setting-up-docker-buildx-in-github-actions">Setting Up Docker Buildx in GitHub Actions<a href="https://www.agas.dev/blog/github-actions-docker-build#setting-up-docker-buildx-in-github-actions" class="hash-link" aria-label="Direct link to Setting Up Docker Buildx in GitHub Actions" title="Direct link to Setting Up Docker Buildx in GitHub Actions" translate="no">​</a></h3>
<p>The docker/setup-buildx-action@v1 action in GitHub Actions is used to set up Docker Buildx in the workflow.</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> Set up Docker Buildx</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> docker/setup</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">buildx</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">action@v1</span><br></span></code></pre></div></div>
<p>By including this step in our workflow file, we enable the GitHub Actions runner to utilize Docker Buildx for building multi-architecture Docker images. This step is critical in ensuring that our Docker images are built correctly for the desired architectures.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="caching-for-efficiency-in-docker-builds">Caching for Efficiency in Docker Builds<a href="https://www.agas.dev/blog/github-actions-docker-build#caching-for-efficiency-in-docker-builds" class="hash-link" aria-label="Direct link to Caching for Efficiency in Docker Builds" title="Direct link to Caching for Efficiency in Docker Builds" translate="no">​</a></h2>
<p>Efficient Docker builds often require a smart caching strategy. This section delves into the use of caching in Docker builds with GitHub Actions, particularly focusing on the <code>actions/cache@v2</code> action.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="caching-docker-layers">Caching Docker Layers<a href="https://www.agas.dev/blog/github-actions-docker-build#caching-docker-layers" class="hash-link" aria-label="Direct link to Caching Docker Layers" title="Direct link to Caching Docker Layers" translate="no">​</a></h3>
<p>Caching is crucial in reducing build time, especially when dealing with Docker images. Docker builds can be time-consuming, primarily due to the downloading of image layers and rebuilding steps. By caching, we can significantly decrease this time.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-caching-works-in-docker">How Caching Works in Docker<a href="https://www.agas.dev/blog/github-actions-docker-build#how-caching-works-in-docker" class="hash-link" aria-label="Direct link to How Caching Works in Docker" title="Direct link to How Caching Works in Docker" translate="no">​</a></h3>
<ul>
<li class=""><strong>Layer Caching</strong>: Docker builds images in layers. By caching these layers, Docker can reuse them in subsequent builds, avoiding the need to rebuild identical layers.</li>
<li class=""><strong>Impact on Build Times</strong>: Caching can drastically reduce build times, making your CI/CD pipeline more efficient.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="implementing-caching-with-actionscachev2">Implementing Caching with <code>actions/cache@v2</code><a href="https://www.agas.dev/blog/github-actions-docker-build#implementing-caching-with-actionscachev2" class="hash-link" aria-label="Direct link to implementing-caching-with-actionscachev2" title="Direct link to implementing-caching-with-actionscachev2" translate="no">​</a></h3>
<p>GitHub Actions provides a caching action, <code>actions/cache@v2</code>, which can be used to cache dependencies and build outputs to improve workflow execution time.</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> Cache Docker layers</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> actions/cache@v2</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token key atrule">path</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> /tmp/.buildx</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">cache</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token key atrule">key</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> runner.os </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">buildx</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">$</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> github.sha </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token key atrule">restore-keys</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">|</span><span class="token scalar string" style="color:rgb(195, 232, 141)"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token scalar string" style="color:rgb(195, 232, 141)">      ${{ runner.os }}-buildx-</span><br></span></code></pre></div></div>
<p>In this snippet, the caching action is configured to cache the Docker layers.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="cache-keys-and-paths">Cache Keys and Paths<a href="https://www.agas.dev/blog/github-actions-docker-build#cache-keys-and-paths" class="hash-link" aria-label="Direct link to Cache Keys and Paths" title="Direct link to Cache Keys and Paths" translate="no">​</a></h3>
<p>Understanding how to effectively use cache keys and paths is vital for maximizing the efficiency of your caching strategy.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="defining-cache-paths">Defining Cache Paths<a href="https://www.agas.dev/blog/github-actions-docker-build#defining-cache-paths" class="hash-link" aria-label="Direct link to Defining Cache Paths" title="Direct link to Defining Cache Paths" translate="no">​</a></h4>
<ul>
<li class=""><strong>Cache Location</strong>: In our workflow, the cache is stored in /tmp/.buildx-cache. This path is where Docker Buildx stores its cache data.</li>
</ul>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="cache-keys">Cache Keys<a href="https://www.agas.dev/blog/github-actions-docker-build#cache-keys" class="hash-link" aria-label="Direct link to Cache Keys" title="Direct link to Cache Keys" translate="no">​</a></h4>
<ul>
<li class=""><strong>Key Generation</strong>: The key for the cache is a unique identifier. In our example, it's a combination of the runner's operating system, buildx, and the SHA of the commit (<code>${{ github.sha }}</code>). This ensures that each build has its unique cache.</li>
<li class=""><strong>Restoration of Cache</strong>: The restore-keys option provides a list of keys to try when restoring the cache. In our case, it attempts to find the most recent cache that matches the specified pattern.</li>
</ul>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="benefits-of-effective-caching">Benefits of Effective Caching<a href="https://www.agas.dev/blog/github-actions-docker-build#benefits-of-effective-caching" class="hash-link" aria-label="Direct link to Benefits of Effective Caching" title="Direct link to Benefits of Effective Caching" translate="no">​</a></h4>
<ul>
<li class=""><strong>Reduced Build Times</strong>: By reusing the cached layers, Docker can skip rebuilding unchanged layers, significantly reducing the overall build time.</li>
<li class=""><strong>Improved Efficiency</strong>: Efficient caching leads to faster CI/CD pipelines, which is crucial for agile development and quick deployment cycles.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="docker-hub-integration-in-github-actions">Docker Hub Integration in GitHub Actions<a href="https://www.agas.dev/blog/github-actions-docker-build#docker-hub-integration-in-github-actions" class="hash-link" aria-label="Direct link to Docker Hub Integration in GitHub Actions" title="Direct link to Docker Hub Integration in GitHub Actions" translate="no">​</a></h2>
<p>Integrating Docker Hub with GitHub Actions is a key step in automating the Docker image build and push process. This section covers the essential steps for secure Docker Hub login and handling secrets within GitHub Actions.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="docker-hub-login">Docker Hub Login<a href="https://www.agas.dev/blog/github-actions-docker-build#docker-hub-login" class="hash-link" aria-label="Direct link to Docker Hub Login" title="Direct link to Docker Hub Login" translate="no">​</a></h3>
<p>Logging into Docker Hub from GitHub Actions is a crucial step to push the built images to your Docker Hub repository. This is where the <code>docker/login-action@v1</code> action comes into play.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="importance-of-secure-login">Importance of Secure Login<a href="https://www.agas.dev/blog/github-actions-docker-build#importance-of-secure-login" class="hash-link" aria-label="Direct link to Importance of Secure Login" title="Direct link to Importance of Secure Login" translate="no">​</a></h3>
<ul>
<li class=""><strong>Security</strong>: Securely logging into Docker Hub ensures that your credentials are protected and your Docker Hub account is safe from unauthorized access.</li>
<li class=""><strong>Automation</strong>: Automated login is essential for seamless CI/CD pipelines, enabling the automated pushing of Docker images to Docker Hub without manual intervention.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="implementing-docker-hub-login">Implementing Docker Hub Login<a href="https://www.agas.dev/blog/github-actions-docker-build#implementing-docker-hub-login" class="hash-link" aria-label="Direct link to Implementing Docker Hub Login" title="Direct link to Implementing Docker Hub Login" translate="no">​</a></h3>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> Login to Docker Hub</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> docker/login</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">action@v1</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token key atrule">username</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> secrets.DOCKERHUB_USERNAME </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token key atrule">password</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> secrets.DOCKERHUB_TOKEN </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></span></code></pre></div></div>
<p>In this step, the docker/login-action@v1 action is used to log into Docker Hub. The credentials are provided through GitHub secrets to ensure security.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="handling-secrets">Handling Secrets<a href="https://www.agas.dev/blog/github-actions-docker-build#handling-secrets" class="hash-link" aria-label="Direct link to Handling Secrets" title="Direct link to Handling Secrets" translate="no">​</a></h3>
<p>GitHub secrets provide a secure way to store and use sensitive information, like Docker Hub credentials, in your GitHub Actions workflows.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-are-github-secrets">What are GitHub Secrets?<a href="https://www.agas.dev/blog/github-actions-docker-build#what-are-github-secrets" class="hash-link" aria-label="Direct link to What are GitHub Secrets?" title="Direct link to What are GitHub Secrets?" translate="no">​</a></h4>
<ul>
<li class=""><strong>Security Feature</strong>: Secrets are encrypted environment variables that you can create in any repository or organization in GitHub. They are not exposed in log files or to unauthorized users.</li>
<li class=""><strong>Usage in Workflows</strong>: Secrets can be used in GitHub Actions workflows to securely manage sensitive data like API keys, passwords, and Docker Hub credentials. Configuring and Using Secrets</li>
<li class=""><strong>Adding Secrets</strong>: Secrets can be added to your GitHub repository under the settings tab. Once added, they can be referenced in your workflow file. Referencing Secrets in Workflows: In the Docker Hub login step, the username and password are referenced as <code>${{ secrets.DOCKERHUB_USERNAME }}</code> and <code>${{ secrets.DOCKERHUB_TOKEN }}</code>. This allows the workflow to use these credentials without exposing them.</li>
</ul>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="benefits-of-using-secrets">Benefits of Using Secrets<a href="https://www.agas.dev/blog/github-actions-docker-build#benefits-of-using-secrets" class="hash-link" aria-label="Direct link to Benefits of Using Secrets" title="Direct link to Benefits of Using Secrets" translate="no">​</a></h4>
<ul>
<li class=""><strong>Enhanced Security</strong>: By using secrets, sensitive information is kept secure and is not exposed in your workflow files or logs.</li>
<li class=""><strong>Flexibility</strong>: Secrets can be easily updated in the GitHub repository settings without changing the workflow files.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="building-and-pushing-the-docker-image">Building and Pushing the Docker Image<a href="https://www.agas.dev/blog/github-actions-docker-build#building-and-pushing-the-docker-image" class="hash-link" aria-label="Direct link to Building and Pushing the Docker Image" title="Direct link to Building and Pushing the Docker Image" translate="no">​</a></h2>
<p>The culmination of our GitHub Actions workflow is the building and pushing of the Docker image. This section provides a detailed walkthrough of this process, highlighting the use of <code>docker/build-push-action@v2</code> and explaining image tagging and multi-architecture support.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="building-the-docker-image">Building the Docker Image<a href="https://www.agas.dev/blog/github-actions-docker-build#building-the-docker-image" class="hash-link" aria-label="Direct link to Building the Docker Image" title="Direct link to Building the Docker Image" translate="no">​</a></h3>
<p>Building the Docker image is a critical step in the workflow. The <code>docker/build-push-action@v2</code> action simplifies this process within GitHub Actions.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="using-dockerbuild-push-actionv2">Using <code>docker/build-push-action@v2</code><a href="https://www.agas.dev/blog/github-actions-docker-build#using-dockerbuild-push-actionv2" class="hash-link" aria-label="Direct link to using-dockerbuild-push-actionv2" title="Direct link to using-dockerbuild-push-actionv2" translate="no">​</a></h3>
<p>This action enables us to build and push the Docker image to a registry like Docker Hub. It supports advanced features like multi-platform builds and caching.</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> Build and push</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> docker/build</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">push</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">action@v2</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token key atrule">context</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> .</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token key atrule">file</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> ./Dockerfile</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token key atrule">push</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token boolean important" style="color:rgb(255, 88, 116)">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token key atrule">tags</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> user/myapp</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain">latest</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> user/myapp</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain">$</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> github.run_number </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token key atrule">platforms</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> linux/amd64</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">linux/arm64/v8</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token key atrule">cache-from</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> type=local</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">src=/tmp/.buildx</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">cache</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token key atrule">cache-to</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> type=local</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">dest=/tmp/.buildx</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">cache</span><br></span></code></pre></div></div>
<p>In this configuration:</p>
<ul>
<li class=""><code>context</code> specifies the build context (typically the root of the repository).</li>
<li class=""><code>file</code> points to the Dockerfile.</li>
<li class=""><code>push</code> set to true enables the pushing of the image to the registry.</li>
<li class=""><code>tags</code> defines the tags for the built image.</li>
<li class=""><code>platforms</code> lists the target architectures.</li>
<li class=""><code>cache-from</code> and <code>cache-to</code> handle caching to speed up the build process.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="tagging-and-multi-architecture-support">Tagging and Multi-Architecture Support<a href="https://www.agas.dev/blog/github-actions-docker-build#tagging-and-multi-architecture-support" class="hash-link" aria-label="Direct link to Tagging and Multi-Architecture Support" title="Direct link to Tagging and Multi-Architecture Support" translate="no">​</a></h3>
<p>Tagging images correctly and supporting multiple architectures are crucial for a robust Docker deployment.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="tagging-images">Tagging Images<a href="https://www.agas.dev/blog/github-actions-docker-build#tagging-images" class="hash-link" aria-label="Direct link to Tagging Images" title="Direct link to Tagging Images" translate="no">​</a></h4>
<ul>
<li class=""><strong>Purpose of Tags</strong>: Tags are used to specify different versions of your Docker images. It's a best practice to tag your images with meaningful identifiers, like version numbers or environment names.</li>
<li class=""><strong>Tag Syntax in GitHub Actions</strong>:</li>
</ul>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token key atrule">tags</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> user/myapp</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain">latest</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> user/myapp</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain">$</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> github.run_number </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></span></code></pre></div></div>
<p>Here, we use two tags - latest for the most recent build and a tag with the GitHub run number for specific builds.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="multi-architecture-support">Multi-Architecture Support<a href="https://www.agas.dev/blog/github-actions-docker-build#multi-architecture-support" class="hash-link" aria-label="Direct link to Multi-Architecture Support" title="Direct link to Multi-Architecture Support" translate="no">​</a></h3>
<p>Supporting multiple architectures ensures that your Docker images can run on various hardware platforms.</p>
<ul>
<li class=""><strong>Specifying Architectures</strong>: In the platforms field of the docker/build-push-action@v2, we specify the target architectures.</li>
</ul>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token key atrule">platforms</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> linux/amd64</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">linux/arm64/v8</span><br></span></code></pre></div></div>
<ul>
<li class=""><strong>Benefits</strong>: This approach ensures that the built Docker images are compatible with both AMD64 and ARM64 architectures, broadening the usability of your application.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion-and-best-practices">Conclusion and Best Practices<a href="https://www.agas.dev/blog/github-actions-docker-build#conclusion-and-best-practices" class="hash-link" aria-label="Direct link to Conclusion and Best Practices" title="Direct link to Conclusion and Best Practices" translate="no">​</a></h2>
<p>We've now reached the end of our comprehensive guide on building and pushing multi-architecture Docker images using GitHub Actions. Let's recap the steps we've taken and go over some best practices and troubleshooting tips.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="review-of-the-process">Review of the Process<a href="https://www.agas.dev/blog/github-actions-docker-build#review-of-the-process" class="hash-link" aria-label="Direct link to Review of the Process" title="Direct link to Review of the Process" translate="no">​</a></h3>
<p>Our journey through this tutorial involved several key steps:</p>
<ol>
<li class=""><strong>Introduction to Docker and GitHub Actions</strong>: We began by understanding the basics of Docker, GitHub Actions, and the need for multi-architecture builds.</li>
<li class=""><strong>Setting Up the Workflow</strong>: We then explored the workflow syntax in GitHub Actions and how to trigger workflows.</li>
<li class=""><strong>Environment Preparation</strong>: Setting up QEMU and Docker Buildx were crucial steps for cross-platform Docker builds.</li>
<li class=""><strong>Caching for Efficiency</strong>: Implementing caching using <code>actions/cache@v2</code> improved our build times significantly.</li>
<li class=""><strong>Docker Hub Integration</strong>: We securely logged into Docker Hub using GitHub secrets, ensuring a secure pipeline.</li>
<li class=""><strong>Building and Pushing Images</strong>: Finally, we used <code>docker/build-push-action@v2</code> to build and push images, tagging them appropriately and supporting multiple architectures.</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="best-practices">Best Practices<a href="https://www.agas.dev/blog/github-actions-docker-build#best-practices" class="hash-link" aria-label="Direct link to Best Practices" title="Direct link to Best Practices" translate="no">​</a></h3>
<p>Following best practices can significantly enhance the efficiency and security of your Docker builds and GitHub Actions workflows.</p>
<h5 class="anchor anchorTargetStickyNavbar_Vzrq" id="docker-builds">Docker Builds<a href="https://www.agas.dev/blog/github-actions-docker-build#docker-builds" class="hash-link" aria-label="Direct link to Docker Builds" title="Direct link to Docker Builds" translate="no">​</a></h5>
<ul>
<li class=""><strong>Use Multi-Stage Builds</strong>: This helps keep your images small and efficient.</li>
<li class=""><strong>Leverage Caching</strong>: Proper caching can drastically reduce build times.</li>
<li class=""><strong>Keep Images Secure</strong>: Regularly update and scan your images for vulnerabilities.</li>
</ul>
<h5 class="anchor anchorTargetStickyNavbar_Vzrq" id="github-actions">GitHub Actions<a href="https://www.agas.dev/blog/github-actions-docker-build#github-actions" class="hash-link" aria-label="Direct link to GitHub Actions" title="Direct link to GitHub Actions" translate="no">​</a></h5>
<ul>
<li class=""><strong>Keep Workflows Simple</strong>: Complex workflows are harder to maintain and debug.</li>
<li class=""><strong>Secure Secrets</strong>: Always use GitHub secrets to handle sensitive information.</li>
<li class=""><strong>Document Your Workflows</strong>: Clear documentation helps maintain and understand workflows.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="troubleshooting-tips">Troubleshooting Tips<a href="https://www.agas.dev/blog/github-actions-docker-build#troubleshooting-tips" class="hash-link" aria-label="Direct link to Troubleshooting Tips" title="Direct link to Troubleshooting Tips" translate="no">​</a></h3>
<p>Here are some common issues and tips for troubleshooting Docker builds and GitHub Actions workflows.</p>
<h5 class="anchor anchorTargetStickyNavbar_Vzrq" id="docker-builds-1">Docker Builds<a href="https://www.agas.dev/blog/github-actions-docker-build#docker-builds-1" class="hash-link" aria-label="Direct link to Docker Builds" title="Direct link to Docker Builds" translate="no">​</a></h5>
<ul>
<li class=""><strong>Build Failures</strong>: Check for syntax errors in your Dockerfile. Ensure all necessary files are included in the build context.</li>
<li class=""><strong>Long Build Times</strong>: Investigate caching strategies and consider optimizing your Dockerfile for better layer caching.</li>
</ul>
<h5 class="anchor anchorTargetStickyNavbar_Vzrq" id="github-actions-1">GitHub Actions<a href="https://www.agas.dev/blog/github-actions-docker-build#github-actions-1" class="hash-link" aria-label="Direct link to GitHub Actions" title="Direct link to GitHub Actions" translate="no">​</a></h5>
<ul>
<li class=""><strong>Workflow Errors</strong>: Review logs provided by GitHub Actions for specific error messages. Ensure all steps are correctly configured.</li>
<li class=""><strong>Secrets Not Working</strong>: Double-check secret names in the repository settings and their references in the workflow file.</li>
</ul>
<hr>
<p><em>With this knowledge, you're now equipped to create efficient, automated Docker workflows using GitHub Actions. Happy coding!</em></p>]]></content:encoded>
            <category>Github Actions</category>
            <category>Docker</category>
            <category>Raspberry Pi Docker</category>
            <category>ARM Docker</category>
            <category>CI/CD</category>
            <category>QEMU</category>
        </item>
        <item>
            <title><![CDATA[From Cloud to Couch, My Blog's Quirky Journey Home]]></title>
            <link>https://www.agas.dev/blog/self-hosted-blog</link>
            <guid>https://www.agas.dev/blog/self-hosted-blog</guid>
            <pubDate>Wed, 15 Nov 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Welcome to my latest digital escapade – a tale that’s part tech, part humor, and entirely real. I’m here to tell you about how my blog, once a mere visitor in the vast cloud, found its way to the cozy confines of my own home. Yes, you read that right. My blog now lives on a Raspberry Pi 4 server, right here under my roof. Let’s dive into this delightful journey of migration, shall we?]]></description>
            <content:encoded><![CDATA[<p>Welcome to my latest digital escapade – a tale that’s part tech, part humor, and entirely real. I’m here to tell you about how my blog, once a mere visitor in the vast cloud, found its way to the cozy confines of my own home. Yes, you read that right. My blog now lives on a Raspberry Pi 4 server, right here under my roof. Let’s dive into this delightful journey of migration, shall we?</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-origin-story">The Origin Story<a href="https://www.agas.dev/blog/self-hosted-blog#the-origin-story" class="hash-link" aria-label="Direct link to The Origin Story" title="Direct link to The Origin Story" translate="no">​</a></h2>
<p>Every great adventure starts with a vision. Mine was built with Docusaurus – a fantastic platform that makes documentation a breeze but also doubles up as a brilliant blog framework. It’s like having a Swiss Army knife, but for blogging. Neat, right?</p>
<p>Now, I must confess, I’m a bit of a neat freak when it comes to my digital life. So, naturally, my blog was dockerized. Think of it like packing your entire house into a single, magical suitcase – ready to move anywhere, anytime. This nifty trick made the migration process smoother than my morning espresso.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-raspberry-homecoming">A Raspberry Homecoming<a href="https://www.agas.dev/blog/self-hosted-blog#a-raspberry-homecoming" class="hash-link" aria-label="Direct link to A Raspberry Homecoming" title="Direct link to A Raspberry Homecoming" translate="no">​</a></h2>
<p>Now, let’s talk about the star of our show – the Raspberry Pi 4. This tiny yet mighty device transformed from a hobbyist's plaything into the powerhouse behind my blog. Hosting a blog on a Raspberry Pi? Sounds crazy, right? But that's exactly what I did. In the quiet corner of my home, this little gadget hums with the life of my blog, proving that you don't need a massive server to make a big impact on the digital stage.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="tunnels-and-clouds">Tunnels and Clouds<a href="https://www.agas.dev/blog/self-hosted-blog#tunnels-and-clouds" class="hash-link" aria-label="Direct link to Tunnels and Clouds" title="Direct link to Tunnels and Clouds" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" src="https://cdn.shizu.id/convert?type=auto&amp;url=https://london.shizu.id/two.webp" alt="cludflare argo tunnel config" class="img_ev3q"></p>
<p>Navigating the complexities of the internet, especially when hosting a website or blog, requires a secure and reliable connection. This is where Cloudflared's Argo Tunnel comes into play in my setup. It's not just any regular internet connection – it's a fortified, encrypted pathway designed to ensure secure and efficient communication between my Raspberry Pi and the wider internet. Imagine a secret underground tunnel, hidden away from the chaos of surface traffic, providing a safe and speedy route for my data. This setup guarantees that my blog remains accessible and secure, avoiding potential cyber threats and ensuring uninterrupted service to my readers.</p>
<p>The domain of a blog is more than just a web address; it’s an integral part of its identity, its brand. For my blog, <a href="http://www.agas.dev/" target="_blank" rel="noopener noreferrer" class="">www.agas.dev</a> is that digital signature, a unique identifier in the vast sea of the internet. Linking this domain to my Raspberry Pi hosted blog was an essential step in establishing its new home. By routing <a href="http://www.agas.dev/" target="_blank" rel="noopener noreferrer" class="">www.agas.dev</a> through the Cloudflared Argo Tunnel, I’ve created a direct and efficient pathway for traffic to reach my blog. It’s akin to setting up a bespoke postal service, one that ensures prompt and secure delivery of content – my digital letters, if you will – to my audience without any detours or delays.</p>
<p>This journey of migration is not just about moving a blog from one hosting environment to another; it's about reimagining what's possible with technology. It's a blend of the old and the new – the tried and tested Raspberry Pi, a favorite among tech hobbyists, coming together with modern solutions like Cloudflared's Argo Tunnel to create a robust, home-based web hosting environment. This blend of technology demonstrates that with the right tools and a bit of ingenuity, you can create a powerful digital presence without relying on traditional, often expensive, hosting solutions.</p>
<p>In essence, this project is a testament to the power of small-scale, home-based technology solutions in a world that's increasingly moving towards decentralization and personal control over digital assets. Hosting my blog on a Raspberry Pi 4, connected securely to the internet through Cloudflared's Argo Tunnel, and accessible via a personalized domain, embodies a shift in how we approach web hosting and content delivery. It’s a shift towards more personal, customizable, and cost-effective solutions, proving that sometimes, the best way to move forward is by bringing things closer to home.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="docker-but-easier">Docker, but Easier<a href="https://www.agas.dev/blog/self-hosted-blog#docker-but-easier" class="hash-link" aria-label="Direct link to Docker, but Easier" title="Direct link to Docker, but Easier" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" src="https://cdn.shizu.id/convert?type=auto&amp;url=https://london.shizu.id/one.webp" alt="portainer config of this blog" class="img_ev3q"></p>
<p>Handling Docker can be a bit like juggling digital cats. To simplify this, I turned to Portainer. It’s a graphical interface that brings simplicity and clarity to managing Docker containers. With Portainer, I could easily update, modify, and maintain my blog’s backend without diving into the command-line abyss. It’s like having a sleek, user-friendly dashboard for your car, making driving (or in this case, managing a blog) a breeze.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="counting-the-costs">Counting the Costs<a href="https://www.agas.dev/blog/self-hosted-blog#counting-the-costs" class="hash-link" aria-label="Direct link to Counting the Costs" title="Direct link to Counting the Costs" translate="no">​</a></h2>
<p>Running a blog doesn't have to break the bank, and my setup is a testament to that. By moving to a Raspberry Pi hosted at home, I've slashed hosting costs without sacrificing performance. For a blog with modest traffic like mine, this is a game-changer. It's like discovering a secret recipe that makes your favorite dish not just tastier but also cheaper to cook.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="you-are-here">You Are Here!<a href="https://www.agas.dev/blog/self-hosted-blog#you-are-here" class="hash-link" aria-label="Direct link to You Are Here!" title="Direct link to You Are Here!" translate="no">​</a></h2>
<p>If you're reading this, you've already become a part of this story. Whether you're accessing my blog directly from my Raspberry Pi server or via Cloudflare's cached version, you're experiencing the fruits of this home-grown, tech-savvy labor. Each click, each visit, is a nod to this unique journey from cloud-based to couch-based blogging.</p>]]></content:encoded>
            <category>Raspberry Pi</category>
            <category>Docker</category>
            <category>Cloudflare</category>
            <category>Portainer</category>
            <category>Docusaurus</category>
            <category>Blog Migration</category>
            <category>Home Server</category>
            <category>Argo Tunnel</category>
            <category>Web Hosting</category>
        </item>
        <item>
            <title><![CDATA[The Navigation Router - The Game-Changing Alternative to React Navigation]]></title>
            <link>https://www.agas.dev/blog/the-navigation-router</link>
            <guid>https://www.agas.dev/blog/the-navigation-router</guid>
            <pubDate>Sat, 18 Feb 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[The Navigation router is a groundbreaking alternative to the popular React Navigation library, offering a revolutionary approach to scene-based navigation for both React and React Native. With its focus on scene-based navigation, this router empowers developers to take control of their app's UI design, providing the flexibility to build their UI in any way they want.]]></description>
            <content:encoded><![CDATA[<p>The Navigation router is a groundbreaking alternative to the popular React Navigation library, offering a revolutionary approach to scene-based navigation for both React and React Native. With its focus on scene-based navigation, this router empowers developers to take control of their app's UI design, providing the flexibility to build their UI in any way they want.</p>
<p>One of the most notable features of the Navigation router is its seamless integration with React and React Native. Developers no longer need to worry about using different routing libraries for web and mobile development, which streamlines the development process and saves time.</p>
<p>Additionally, the Navigation router provides a seamless user experience. The navigation is 100% native on Android and iOS, ensuring that users feel comfortable and familiar with the navigation elements. On the other hand, developers using React can have whatever URLs they want, allowing them to build their UI as they see fit.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-navigation-routers-unique-approach">The Navigation Router's Unique Approach<a href="https://www.agas.dev/blog/the-navigation-router#the-navigation-routers-unique-approach" class="hash-link" aria-label="Direct link to The Navigation Router's Unique Approach" title="Direct link to The Navigation Router's Unique Approach" translate="no">​</a></h2>
<p>The Navigation Router offers a more native feel to web and mobile applications as it avoids using a single View in its codebase, rendering everything to its native counterpart. Additionally, the Router's use of a CoordinatorLayout provides native collapsing Toolbars and TabBars on Android, with the TabBar component being 100% native on both platforms.</p>
<p>In keeping with its focus on React, the Navigation Router makes every UI element a component. This includes TabBar and NavigationBar components, which can be easily updated using props in the same manner as other React components. Each scene has its own Navigation context, preventing the router from pushing the same scene twice even when double-tapping.</p>
<p>The Navigation Router's focus on the web also ensures that every single navigation is a URL. This provides automatic hyperlinks on all platforms, including for back navigation. Developers can customize the URLs as desired without changing any code, passing strongly-typed data without the need to parse to or from strings.</p>
<p>Overall, the Navigation Router's focus on providing a native, React, and web-friendly experience makes it an innovative choice for developers seeking to create seamless, high-performance applications across multiple platforms.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="using-the-navigation-router-on-react-native">Using The Navigation Router on React Native.<a href="https://www.agas.dev/blog/the-navigation-router#using-the-navigation-router-on-react-native" class="hash-link" aria-label="Direct link to Using The Navigation Router on React Native." title="Direct link to Using The Navigation Router on React Native." translate="no">​</a></h2>
<p>If you're a developer looking to create a high-performing, fully-native navigation experience for your React Native app, the Navigation router is the solution you've been searching for. This powerful library offers an out-of-the-box experience that rivals that of building a native app without React Native. With its TabBar component rendering to the native UITabBarController from UIKit or the BottomNavigationView from Android's Material Design library, users will feel right at home within your app.</p>
<p>To take advantage of the Navigation router's capabilities, it's important to note that it contains native code. Therefore, to create your application, you must use react-native init or a custom development build of Expo. But with its powerful features and seamless integration, the Navigation router is well worth the effort.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="quick-getting-started">Quick Getting Started<a href="https://www.agas.dev/blog/the-navigation-router#quick-getting-started" class="hash-link" aria-label="Direct link to Quick Getting Started" title="Direct link to Quick Getting Started" translate="no">​</a></h3>
<p>Install the required packages in your React Native project:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">npm install navigation navigation</span><span class="token operator" style="color:rgb(137, 221, 255)">-</span><span class="token plain">react navigation</span><span class="token operator" style="color:rgb(137, 221, 255)">-</span><span class="token plain">react</span><span class="token operator" style="color:rgb(137, 221, 255)">-</span><span class="token plain">native</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">cd ios </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;&amp;</span><span class="token plain"> pod install </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;&amp;</span><span class="token plain"> cd </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><br></span></code></pre></div></div>
<p>The Navigation router is comprised of core utilities that are used by navigators to build the navigation structure for your app. Once you've completed this initial setup, you'll be ready to start writing code with the Navigation router.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="wrapping-your-app-in-navigationhandler">Wrapping your app in <code>NavigationHandler</code><a href="https://www.agas.dev/blog/the-navigation-router#wrapping-your-app-in-navigationhandler" class="hash-link" aria-label="Direct link to wrapping-your-app-in-navigationhandler" title="Direct link to wrapping-your-app-in-navigationhandler" translate="no">​</a></h3>
<div class="language-jsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-jsx codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token keyword module" style="font-style:italic">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">React</span><span class="token plain"> </span><span class="token keyword module" style="font-style:italic">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'react'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword module" style="font-style:italic">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token imports maybe-class-name">StateNavigator</span><span class="token imports punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"> </span><span class="token keyword module" style="font-style:italic">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'navigation'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword module" style="font-style:italic">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token imports maybe-class-name">NavigationHandler</span><span class="token imports punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"> </span><span class="token keyword module" style="font-style:italic">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'navigation-react'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword module" style="font-style:italic">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token imports maybe-class-name">NavigationStack</span><span class="token imports punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"> </span><span class="token keyword module" style="font-style:italic">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'navigation-react-native'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> stateNavigator </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">StateNavigator</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token function-variable function maybe-class-name" style="color:rgb(130, 170, 255)">App</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:rgb(137, 221, 255)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token tag punctuation" style="color:rgb(199, 146, 234)">&lt;</span><span class="token tag class-name" style="color:rgb(255, 203, 107)">NavigationHandler</span><span class="token tag" style="color:rgb(255, 85, 114)"> </span><span class="token tag attr-name" style="color:rgb(255, 203, 107)">stateNavigator</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:rgb(199, 146, 234)">=</span><span class="token tag script language-javascript punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token tag script language-javascript" style="color:rgb(255, 85, 114)">stateNavigator</span><span class="token tag script language-javascript punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token tag punctuation" style="color:rgb(199, 146, 234)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:rgb(199, 146, 234)">&lt;</span><span class="token tag class-name" style="color:rgb(255, 203, 107)">NavigationStack</span><span class="token tag punctuation" style="color:rgb(199, 146, 234)">&gt;</span><span class="token tag punctuation" style="color:rgb(199, 146, 234)">&lt;/</span><span class="token tag class-name" style="color:rgb(255, 203, 107)">NavigationStack</span><span class="token tag punctuation" style="color:rgb(199, 146, 234)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain-text">  </span><span class="token tag punctuation" style="color:rgb(199, 146, 234)">&lt;/</span><span class="token tag class-name" style="color:rgb(255, 203, 107)">NavigationHandler</span><span class="token tag punctuation" style="color:rgb(199, 146, 234)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword module" style="font-style:italic">export</span><span class="token plain"> </span><span class="token keyword module" style="font-style:italic">default</span><span class="token plain"> </span><span class="token maybe-class-name">App</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></span></code></pre></div></div>
<p>To ensure optimal performance in your React Native app, it's recommended that you only use the NavigationHandler once at the root level. Nesting multiple NavigationHandler components should be avoided unless there is a specific use case for doing so. <strong>With this in mind, you're now ready to build and run your app on your device or simulator.</strong></p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="pro-tips">Pro Tips<a href="https://www.agas.dev/blog/the-navigation-router#pro-tips" class="hash-link" aria-label="Direct link to Pro Tips" title="Direct link to Pro Tips" translate="no">​</a></h4>
<p>To incorporate Material Design 3 into your Android app, you can configure a Material3 theme in styles.xml. Here's an example:</p>
<div class="language-jsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-jsx codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token tag punctuation" style="color:rgb(199, 146, 234)">&lt;</span><span class="token tag" style="color:rgb(255, 85, 114)">style</span><span class="token tag" style="color:rgb(255, 85, 114)"> </span><span class="token tag attr-name" style="color:rgb(255, 203, 107)">name</span><span class="token tag attr-value punctuation attr-equals" style="color:rgb(199, 146, 234)">=</span><span class="token tag attr-value punctuation" style="color:rgb(199, 146, 234)">"</span><span class="token tag attr-value" style="color:rgb(255, 85, 114)">AppTheme</span><span class="token tag attr-value punctuation" style="color:rgb(199, 146, 234)">"</span><span class="token tag" style="color:rgb(255, 85, 114)"> </span><span class="token tag attr-name" style="color:rgb(255, 203, 107)">parent</span><span class="token tag attr-value punctuation attr-equals" style="color:rgb(199, 146, 234)">=</span><span class="token tag attr-value punctuation" style="color:rgb(199, 146, 234)">"</span><span class="token tag attr-value" style="color:rgb(255, 85, 114)">Theme.Material3.DayNight.NoActionBar</span><span class="token tag attr-value punctuation" style="color:rgb(199, 146, 234)">"</span><span class="token tag punctuation" style="color:rgb(199, 146, 234)">&gt;</span><br></span></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="enabling-fabric-the-new-architecture">Enabling Fabric (The New Architecture)<a href="https://www.agas.dev/blog/the-navigation-router#enabling-fabric-the-new-architecture" class="hash-link" aria-label="Direct link to Enabling Fabric (The New Architecture)" title="Direct link to Enabling Fabric (The New Architecture)" translate="no">​</a></h4>
<p>Enabling the new React Native architecture during pod install is the only difference in setup from the previous architecture.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">cd ios &amp;&amp; RCT_NEW_ARCH_ENABLED=1 pod install &amp;&amp; cd ..</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="summary">Summary<a href="https://www.agas.dev/blog/the-navigation-router#summary" class="hash-link" aria-label="Direct link to Summary" title="Direct link to Summary" translate="no">​</a></h2>
<p>The Navigation router is a revolutionary library that empowers developers to take control of their app's UI design by offering a groundbreaking approach to scene-based navigation for both React and React Native.</p>
<p>The Navigation router's integration with React and React Native makes it an innovative choice for developers seeking to create seamless, high-performance applications across multiple platforms. Overall, the Navigation router's unique features and powerful capabilities make it a must-have tool for developers looking to create high-performing and fully-native navigation experiences for their React Native apps.</p>
<p><strong>The Navigation router is a worthy alternative to the popular React Navigation library</strong>. Its unique approach to scene-based navigation and native feel make it an innovative choice for creating high-performance apps across multiple platforms.</p>]]></content:encoded>
            <category>navigation</category>
            <category>react-native</category>
            <category>brownfield</category>
            <category>greenfield</category>
            <category>react-navigation</category>
        </item>
        <item>
            <title><![CDATA[Experimenting with React Native at Ruangguru]]></title>
            <link>https://www.agas.dev/blog/react-native-at-ruangguru</link>
            <guid>https://www.agas.dev/blog/react-native-at-ruangguru</guid>
            <pubDate>Wed, 28 Sep 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[This is the story of why we chose React Native and how we overcame the technical challenges we encountered along the road.]]></description>
            <content:encoded><![CDATA[<p>This is the story of why we chose React Native and how we overcame the technical challenges we encountered along the road.</p>
<p><img decoding="async" loading="lazy" src="https://cdn.shizu.id/convert?type=auto&amp;width=900&amp;url=https://london.shizu.id/1_3e2_XdcGIwQBGcJxIkHLPg.png" alt="ruangguru react native" class="img_ev3q"></p>
<p>BrainAcademy build with React Native running on our existing native app.</p>
<p>Early last year, a small group of developers at Ruangguru began studying the feasibility of implementing React Native. React Native allows you to create iOS and Android apps in JavaScript using the declarative programming language of React. This leads in more concise, easier-to-understand code, faster iteration without a build cycle, and easy code sharing between platforms. You can ship faster and focus on the things that count, making your app look and feel fantastic.</p>
<p>Since its initial release, React Native has come a long way. It is being used not only in Facebook and Instagram, but also in a wide range of other businesses, from Fortune 500 corporations to hot new startups.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-reasons">The Reasons<a href="https://www.agas.dev/blog/react-native-at-ruangguru#the-reasons" class="hash-link" aria-label="Direct link to The Reasons" title="Direct link to The Reasons" translate="no">​</a></h3>
<p>We began investigating the use of React Native in early 2022 to allow product teams to release features faster through code sharing and faster iteration speeds, using techniques like Live Reload and Hot Reloading that remove compile-install cycles. <strong>As a result, the product engineering team is now able to deploy the same functionality on iOS and Android with a single codebase</strong>, and perhaps in the future, we will be able to ship the same React Native codebase to the web. So with React Native the App feature will be written only once.</p>
<p><img decoding="async" loading="lazy" src="https://cdn-images-1.medium.com/max/1600/1*L0AkO9LL3wdYpOD3Mq46qw.png" alt="single codebase example" class="img_ev3q"></p>
<p>Ruangguru universal frontend. A teaser for our single codebase multiplatform solution.</p>
<p>The feature created with React Native will be injected into the app at the same time working side by side with the existing native code. The cross-platform feature is developed and maintained by a single team. So there’s no longer a large panel of native engineer experts involved.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-challenges">The Challenges<a href="https://www.agas.dev/blog/react-native-at-ruangguru#the-challenges" class="hash-link" aria-label="Direct link to The Challenges" title="Direct link to The Challenges" translate="no">​</a></h3>
<p>When you integrate React Native into an existing native project, you may experience obstacles and additional effort that you do not encounter when you start from scratch. With this in mind, we chose to begin investigating these challenges by porting the most appropriate part of Ruangguru apps: the Brain Academy module.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="slow-communications-between-native-and-react-native">Slow Communications Between Native and React Native<a href="https://www.agas.dev/blog/react-native-at-ruangguru#slow-communications-between-native-and-react-native" class="hash-link" aria-label="Direct link to Slow Communications Between Native and React Native" title="Direct link to Slow Communications Between Native and React Native" translate="no">​</a></h4>
<p>If you look at the React Native layout, you’ll notice that it uses a Bridge to connect with native modules, which often results in a queue because these two sides are unaware of each other. Such a process may result in performance constraints that have a detrimental impact on the user experience.</p>
<p>However, with the introduction of the React Native re-architecture, the Bridge will be gradually phased out and replaced by a new component known as the JavaScript Interface (JSI). Ruangguru will utilize JSI to facilitate communication between the Native and Javascirpt Realms to overcome this challenge.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="start-up-performance">Start Up Performance<a href="https://www.agas.dev/blog/react-native-at-ruangguru#start-up-performance" class="hash-link" aria-label="Direct link to Start Up Performance" title="Direct link to Start Up Performance" translate="no">​</a></h4>
<p>Previously, React Native had a significant startup overhead due to the need to inject the JavaScript bundle into JavaScriptCore (the VM used by React Native on both iOS and Android) and instantiate native modules and view managers. This problem is dramatically reduced in 2022 thanks to the Hermes engine. Based on our testing the initial loading for React Native is very minimal.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="android-bundle-size">Android Bundle Size<a href="https://www.agas.dev/blog/react-native-at-ruangguru#android-bundle-size" class="hash-link" aria-label="Direct link to Android Bundle Size" title="Direct link to Android Bundle Size" translate="no">​</a></h4>
<p>The first issue was adding React Native as a dependency into an existing App would significant impact the binary size. But this would not only increase the binary size, but would also have a significant impact on the number of methods count. We ended up pulling in only the React Native pieces we required at the time and building our own implementations for those that relied on libraries we didn’t want to use.</p>
<p>However, this solution still adds megabytes to the app binary size. As a result, we will include the React Native module with the Dynamic Features Module. Because we made React Native an on-demand module, users will only need to download the React Native module when they need to use the feature. This significantly reduces the app install size.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="utilizing-the-new-react-native-architecture"><strong>Utilizing The New React Native Architecture</strong><a href="https://www.agas.dev/blog/react-native-at-ruangguru#utilizing-the-new-react-native-architecture" class="hash-link" aria-label="Direct link to utilizing-the-new-react-native-architecture" title="Direct link to utilizing-the-new-react-native-architecture" translate="no">​</a></h3>
<p>This section will discuss how Ruangguru uses JSI, a component of the new React Native architecture, to make navigation events, sharing data, and resource synchronization considerably faster than before.</p>
<p><strong>The Old Architecture</strong></p>
<p>The Javascript and Main threads do not communicate directly under the old architecture, but rely on the Bridge. The existence of a serializable bridge results in wasteful copying rather than memory sharing between threads. Because everything runs asynchronously, there is no guarantee that the data will arrive on time or at all. Larger data transfers may be extremely slow. Native module features that need synchronous data access cannot be fully utilized.</p>
<p><strong>The New Architecture</strong></p>
<p>The new architecture completely removed the Bridge. Javascript can now store references to C++ Host objects and call their methods. Time-sensitive tasks, such as user interaction with the app, can be carried out synchronously, eliminating the need for serialization and deserialization during runtime.</p>
<p><strong>Next Generation Native Module That Ruangguru Uses.</strong></p>
<p>All Native Module that Ruangguru uses is using JSI the backbone of React Native’s new architecture.</p>
<p><img decoding="async" loading="lazy" src="https://cdn-images-1.medium.com/max/1600/1*IkM5IvaH3rpNi5Ppga86mw.png" alt="JSI Code Example" class="img_ev3q"></p>
<p>Initializing a JSI module.</p>
<p>The process of initializing a JSI module differs from that of an ordinary React Native Module. We have an install method here that will be called once from the JavaScript realm to initialize the “connection.”</p>
<p>The JavaScript realm must first check to see if the module is appropriately installed; if the module is not present, the JavaScript realm will invoke the install method.</p>
<p><img decoding="async" loading="lazy" src="https://cdn-images-1.medium.com/max/1600/1*DvzkAuAb31qyw4q3_yDaXw.png" alt="JSI Code Example 2" class="img_ev3q"></p>
<p>jsiRuntime.global().setProperty method.</p>
<p>Because we’re utilizing JSI, we’ll utilize the install method to initialize the native function we want to share between the Native and JavaScript realms. As you can see in the code, we use the jsiRuntime.global().setProperty method to share the function between the two realms.</p>
<p><img decoding="async" loading="lazy" src="https://cdn-images-1.medium.com/max/1600/1*roM83iXWQFUPP_bkIEKpgQ.png" alt="JSI Code Example 3" class="img_ev3q"></p>
<p>So finally, to use this on the JavaScript Realm, we can simply use the method like the picture above. So, in short, with the new architecture, we eliminate the need for the old react native bridge, eliminating the waiting time for the Asynchronous method to resolve, and enable instant communication between the Native and JavaScript realm.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="results">Results<a href="https://www.agas.dev/blog/react-native-at-ruangguru#results" class="hash-link" aria-label="Direct link to Results" title="Direct link to Results" translate="no">​</a></h3>
<p>React Native enabled product teams to release features to both our iOS and Android apps more quickly.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="startup-time">Startup Time<a href="https://www.agas.dev/blog/react-native-at-ruangguru#startup-time" class="hash-link" aria-label="Direct link to Startup Time" title="Direct link to Startup Time" translate="no">​</a></h4>
<p><img decoding="async" loading="lazy" src="https://cdn-images-1.medium.com/max/1600/1*dwxayAioDPVVFPOAcosgMQ.gif" alt="Startup Image" class="img_ev3q"></p>
<p>React Native cold startup on Android.</p>
<p>After enabling Hermes and preloading on react native bundle, we can achieve desirable startup time when switching pages from Native to React Native. Users should not be able to distinguish the loading time between a Native and React Native screen.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="navigation"><strong>Navigation</strong><a href="https://www.agas.dev/blog/react-native-at-ruangguru#navigation" class="hash-link" aria-label="Direct link to navigation" title="Direct link to navigation" translate="no">​</a></h4>
<p><img decoding="async" loading="lazy" src="https://cdn-images-1.medium.com/max/1600/1*M0ca-P44EtLFu98XVeUtRw.gif" alt="Native Navigation" class="img_ev3q"></p>
<p>BrainAcademy Navigation example.</p>
<p>Navigation between React Native to Native screen and vice versa is rapid and should not be noticeable to the users. As shown in the demo above, the navigation from BrainAcademy live teaching list (react-native) to the live teaching front page (native) is as fast as usual native navigation.</p>
<p>In the example above we demonstrated the capabilities of Ruangguru React Native navigation.</p>
<p>BrainAcademy Home (RN) -&gt; Live Teaching Front Page (Native) -&gt; Cek Ombak (RN).</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="dynamic-feature-module-android">Dynamic Feature Module (Android)<a href="https://www.agas.dev/blog/react-native-at-ruangguru#dynamic-feature-module-android" class="hash-link" aria-label="Direct link to Dynamic Feature Module (Android)" title="Direct link to Dynamic Feature Module (Android)" translate="no">​</a></h4>
<p><img decoding="async" loading="lazy" src="https://cdn-images-1.medium.com/max/1600/1*K3tO0LTQ7058YrBY3HKjeQ.gif" alt="Android DFM" class="img_ev3q"></p>
<p>Dynamic Feature Module for BrainAcademy React Native.</p>
<p>Because BrainAcademy React Native is an on-demand module, users will only need to download it when they need to use the feature. With DFM enabled, we drastically minimize the cost of React Native on android.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-future">The Future<a href="https://www.agas.dev/blog/react-native-at-ruangguru#the-future" class="hash-link" aria-label="Direct link to The Future" title="Direct link to The Future" translate="no">​</a></h3>
<p>Every decision always involves trade-offs, and we recognize that there are no silver bullets when selecting a technology, but we believe the benefits exceed the drawbacks. We are very optimistic about Ruangguru’s future with React Native.</p>
<p>With the expected productivity increases, we may begin to build product feature teams with cross-functional team members collaborating to address current and future business difficulties. Soon, we will also investigate how to leverage React Native javascript ecosystem into the greater Web ecosystem. Because React Native leverage and use the existing web ecosystem, we are thinking of shipping the same codebase to the web.</p>
<p>We think that by implementing React Native, we will be on the most efficient and effective route to developing the Ruangguru family of apps that satisfy the demands of our customers.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="footnote">Footnote<a href="https://www.agas.dev/blog/react-native-at-ruangguru#footnote" class="hash-link" aria-label="Direct link to Footnote" title="Direct link to Footnote" translate="no">​</a></h3>
<p><em>Please visit our</em> <a href="https://career.ruangguru.com/" target="_blank" rel="noopener noreferrer" class=""><em>careers</em></a> <em>page if this blog article piqued your interest in what we’re doing.</em></p>
<p><a href="https://github.com/gedeagas" target="_blank" rel="noopener noreferrer" class=""><em>Agastya Darma</em></a><em>,</em> <a href="https://github.com/seanzeliq" target="_blank" rel="noopener noreferrer" class=""><em>Sean Urian</em></a> <em>and</em> <a href="https://github.com/jevhee" target="_blank" rel="noopener noreferrer" class=""><em>Jevi Saputra</em></a> are Software Engineers working on the Core React Native team at Ruangguru.</p>]]></content:encoded>
            <category>ruangguru</category>
            <category>react-native</category>
            <category>brownfield</category>
            <category>greenfield</category>
            <category>turbomodules</category>
            <category>jsi</category>
        </item>
        <item>
            <title><![CDATA[Recoil, A State Management Library For React]]></title>
            <link>https://www.agas.dev/blog/recoil-state-management</link>
            <guid>https://www.agas.dev/blog/recoil-state-management</guid>
            <pubDate>Sun, 08 Aug 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[Biggest Challenge in React application is the management of global state. In large applications, React alone is not sufficient to handle the state complexity which is why some developers use React hooks, Redux and others state management libraries.]]></description>
            <content:encoded><![CDATA[<p>Biggest Challenge in React application is the management of global state. In large applications, React alone is not sufficient to handle the state complexity which is why some developers use React hooks, Redux and others state management libraries.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="do-you-need-a-state-management-library">Do You Need A State Management Library?<a href="https://www.agas.dev/blog/recoil-state-management#do-you-need-a-state-management-library" class="hash-link" aria-label="Direct link to Do You Need A State Management Library?" title="Direct link to Do You Need A State Management Library?" translate="no">​</a></h3>
<p>For reasons of compatibility and simplicity, it's best to use React's built-in state management capabilities rather than external global state like Recoil. But as i said before React has certain limitations when it comes to a global state management.</p>
<ul>
<li class="">
<p>Component state can only be shared by pushing it up to the common ancestor, but this might include a huge tree that then needs to re-render.</p>
</li>
<li class="">
<p>Context can only store a single value, not an indefinite set of values each with its own consumers.</p>
</li>
<li class="">
<p>Both of these make it difficult to code-split the top of the tree (where the state has to live) from the leaves of the tree (where the state is used).</p>
</li>
</ul>
<h5 class="anchor anchorTargetStickyNavbar_Vzrq" id="so-when-should-we-use-a-state-management-library-like-recoil">So When Should We Use A State Management Library Like Recoil?<a href="https://www.agas.dev/blog/recoil-state-management#so-when-should-we-use-a-state-management-library-like-recoil" class="hash-link" aria-label="Direct link to So When Should We Use A State Management Library Like Recoil?" title="Direct link to So When Should We Use A State Management Library Like Recoil?" translate="no">​</a></h5>
<p>Applying a global state management is not so easy, it is a lot off hard work and it also takes time to implement. So, it is very important for you to know when to implement the state-management.</p>
<ol>
<li class="">If your application contains a large number of components and a lot of requests are being sent to the back-end for data retrieval, then it becomes mandatory to implement the state-management, as it will boost the user experience and speed of the application to a great extent. With a global state, you don't have to fetch the same request multiple times as the data will already be "cached" from the first request and can be consumed by other part of your screen.</li>
<li class="">If you use redundant data throughout the whole app, for example, a list of customers is being used in the invoice creation and sales report generation then there is no need to fetch customers again and again from the database. You could simply just put the data in the global state.</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-is-it-about-recoiljs-thats-so-appealing">What is it about Recoil.js that’s so appealing?<a href="https://www.agas.dev/blog/recoil-state-management#what-is-it-about-recoiljs-thats-so-appealing" class="hash-link" aria-label="Direct link to What is it about Recoil.js that’s so appealing?" title="Direct link to What is it about Recoil.js that’s so appealing?" translate="no">​</a></h3>
<p>Recoil feels just like React. The syntax is similar to React and it looks like a part of <a href="https://medium.com/habilelabs/brahmos-js-a-new-frontend-ui-framework-with-react-like-api-8d1309b17de" target="_blank" rel="noopener noreferrer" class="">React API</a>. Other than that, it has many other upsides like it solves the problem of global state management, shared state, derived data, etc. The team at Recoil make sure that the semantics and behavior of Recoil be as Reactish as possible.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-recoil-concept">The Recoil Concept.<a href="https://www.agas.dev/blog/recoil-state-management#the-recoil-concept" class="hash-link" aria-label="Direct link to The Recoil Concept." title="Direct link to The Recoil Concept." translate="no">​</a></h3>
<p>Recoil is an <strong>experimental</strong> state management library at Facebook, created by Dave McCabe. The reason why i like Recoil better than Redux is because Recoil solves all our complex state management problems but its configuration is surprisingly simple, unlike Redux. And we do not need to write much boilerplate code as we would have by using other state management library like Redux.</p>
<h5 class="anchor anchorTargetStickyNavbar_Vzrq" id="installing-recoil">Installing Recoil<a href="https://www.agas.dev/blog/recoil-state-management#installing-recoil" class="hash-link" aria-label="Direct link to Installing Recoil" title="Direct link to Installing Recoil" translate="no">​</a></h5>
<p>As Recoil is a state management library for React, you need to make sure that you have React or React Native installed and running before getting started.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">npm install recoil</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">// or</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">yarn add recoil</span><br></span></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="core-concept-of-recoil">Core Concept of Recoil<a href="https://www.agas.dev/blog/recoil-state-management#core-concept-of-recoil" class="hash-link" aria-label="Direct link to Core Concept of Recoil" title="Direct link to Core Concept of Recoil" translate="no">​</a></h4>
<p>There are two core concepts of Recoil that you need to understand. This are Atoms and Selectors.</p>
<h5 class="anchor anchorTargetStickyNavbar_Vzrq" id="atoms">Atoms<a href="https://www.agas.dev/blog/recoil-state-management#atoms" class="hash-link" aria-label="Direct link to Atoms" title="Direct link to Atoms" translate="no">​</a></h5>
<p>Atoms are units of state. They're updateable and subscribable: when an atom is updated, each subscribed component is re-rendered with the new value. They can be created at runtime, too. Atoms can be used in place of React local component state. If the same atom is used from multiple components, all those components share their state.</p>
<p>You can create Atoms with the <code>atom</code> function:</p>
<div class="language-tsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-tsx codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> countState </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">atom</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  key</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'countState'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token keyword" style="font-style:italic">default</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">1</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></span></code></pre></div></div>
<p>Atoms use a unique key for debugging, persistence, and mapping of all atoms. <strong>You can't have a duplicate key among the atoms</strong>. So because of that you need to make sure they're globally unique. And also like a React component state, they also have a default value.</p>
<p>To read and write an atom from a component, we use a hook called <code>useRecoilState</code>. It's just like React's <code>useState</code>, but now the state can be shared between components:</p>
<div class="language-tsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-tsx codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">CountButton</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">countValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> setCountValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">useRecoilState</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">countState</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token tag punctuation" style="color:rgb(199, 146, 234)">&lt;</span><span class="token tag punctuation" style="color:rgb(199, 146, 234)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:rgb(199, 146, 234)">&lt;</span><span class="token tag" style="color:rgb(255, 85, 114)">h4</span><span class="token tag punctuation" style="color:rgb(199, 146, 234)">&gt;</span><span class="token plain-text">Count Value </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain">countValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token tag punctuation" style="color:rgb(199, 146, 234)">&lt;/</span><span class="token tag" style="color:rgb(255, 85, 114)">h4</span><span class="token tag punctuation" style="color:rgb(199, 146, 234)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:rgb(199, 146, 234)">&lt;</span><span class="token tag" style="color:rgb(255, 85, 114)">button</span><span class="token tag" style="color:rgb(255, 85, 114)"> </span><span class="token tag attr-name" style="color:rgb(255, 203, 107)">onClick</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:rgb(199, 146, 234)">=</span><span class="token tag script language-javascript punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token tag script language-javascript punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token tag script language-javascript punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token tag script language-javascript" style="color:rgb(255, 85, 114)"> </span><span class="token tag script language-javascript arrow operator" style="color:rgb(137, 221, 255)">=&gt;</span><span class="token tag script language-javascript" style="color:rgb(255, 85, 114)"> </span><span class="token tag script language-javascript function" style="color:rgb(130, 170, 255)">setCountValue</span><span class="token tag script language-javascript punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token tag script language-javascript punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token tag script language-javascript" style="color:rgb(255, 85, 114)">value</span><span class="token tag script language-javascript punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token tag script language-javascript" style="color:rgb(255, 85, 114)"> </span><span class="token tag script language-javascript arrow operator" style="color:rgb(137, 221, 255)">=&gt;</span><span class="token tag script language-javascript" style="color:rgb(255, 85, 114)"> value </span><span class="token tag script language-javascript operator" style="color:rgb(137, 221, 255)">+</span><span class="token tag script language-javascript" style="color:rgb(255, 85, 114)"> </span><span class="token tag script language-javascript number" style="color:rgb(247, 140, 108)">1</span><span class="token tag script language-javascript punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token tag script language-javascript punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token tag punctuation" style="color:rgb(199, 146, 234)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain-text">        Click to Increase Count</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:rgb(199, 146, 234)">&lt;/</span><span class="token tag" style="color:rgb(255, 85, 114)">button</span><span class="token tag punctuation" style="color:rgb(199, 146, 234)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:rgb(199, 146, 234)">&lt;/</span><span class="token tag punctuation" style="color:rgb(199, 146, 234)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></span></code></pre></div></div>
<h5 class="anchor anchorTargetStickyNavbar_Vzrq" id="selectors">Selectors<a href="https://www.agas.dev/blog/recoil-state-management#selectors" class="hash-link" aria-label="Direct link to Selectors" title="Direct link to Selectors" translate="no">​</a></h5>
<p>A selector is basically a piece of derived state, where ‘derived state’ can be defined as the ‘the output of passing state to a pure function that modifies the given state in some way’. So in short when these upstream atoms or selectors are updated, the selector function will be re-evaluated. Components can subscribe to selectors just like atoms, and will then be re-rendered when the selectors change.</p>
<div class="language-tsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-tsx codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> countLabelOddEventState </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">selector</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  key</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'countLabelOddEventState'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token function-variable function" style="color:rgb(130, 170, 255)">get</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain">get</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:rgb(137, 221, 255)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> count </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">get</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">countState</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">count </span><span class="token operator" style="color:rgb(137, 221, 255)">%</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">2</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">==</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(195, 232, 141)">`</span><span class="token template-string string" style="color:rgb(195, 232, 141)">isEven</span><span class="token template-string template-punctuation string" style="color:rgb(195, 232, 141)">`</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(195, 232, 141)">`</span><span class="token template-string string" style="color:rgb(195, 232, 141)">isOdd</span><span class="token template-string template-punctuation string" style="color:rgb(195, 232, 141)">`</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></span></code></pre></div></div>
<p>As you can see Selectors also have a unique ID like atoms <strong>but not a default value</strong>. A selector takes atoms or other selectors as input and when these inputs are updated, the selector function gets re-evaluated.</p>
<p>The <code>get</code> property is the function that is to be computed. It can access the value of atoms and other selectors using the <code>get</code> argument passed to it. Whenever it accesses another atom or selector, a dependency relationship is created such that updating the other atom or selector will cause this one to be recomputed.</p>
<p>Selectors can be read using <code>useRecoilValue()</code>, which takes an atom or selector as an argument and returns the corresponding value. We don't use the <code>useRecoilState()</code> as the <code>countLabelOddEventState</code> <strong>selector is not writeable</strong> (see the <a href="https://recoiljs.org/docs/api-reference/core/selector" target="_blank" rel="noopener noreferrer" class="">selector API reference</a> for more information on writeable selectors).</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://www.agas.dev/blog/recoil-state-management#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h3>
<p>Personally i think Recoil is a great library but unless you have some specific problems regarding global state management, you don’t really need it. It's nothing that the developer’s world couldn’t survive without. You can even use Recoil partially in your application, exactly where you need, without having to adopt it for the entire application.</p>
<h5 class="anchor anchorTargetStickyNavbar_Vzrq" id="references">References<a href="https://www.agas.dev/blog/recoil-state-management#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h5>
<p><a href="https://recoiljs.org/docs/introduction/core-concepts" target="_blank" rel="noopener noreferrer" class="">Recoil Core Concepts</a>.</p>
<p><a href="https://medium.com/habilelabs/recoil-js-a-new-state-management-library-for-react-3319ca42356f" target="_blank" rel="noopener noreferrer" class="">Recoil.js — A New State Management Library for React</a>.</p>]]></content:encoded>
            <category>recoil</category>
            <category>react</category>
            <category>react-native</category>
            <category>state-management</category>
        </item>
        <item>
            <title><![CDATA[React Native Code-Splitting With Repack]]></title>
            <link>https://www.agas.dev/blog/repack</link>
            <guid>https://www.agas.dev/blog/repack</guid>
            <pubDate>Thu, 05 Aug 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[When you are developing a React Native application you will most likely be writing a lot of JavaScript code that contains dependencies that usually come from external repositories like NPM. The compilation of these many JavaScript files and dependencies will be processed into a single bundle file that can be read by React Native. In React Native this compilation will be done by default by Metro Bundler.]]></description>
            <content:encoded><![CDATA[<p>When you are developing a React Native application you will most likely be writing a lot of JavaScript code that contains dependencies that usually come from external repositories like NPM. The compilation of these many JavaScript files and dependencies will be processed into a single <em>bundle</em> file that can be read by React Native. In React Native this compilation will be done by default by <strong><a href="https://facebook.github.io/metro/" target="_blank" rel="noopener noreferrer" class="">Metro Bundler</a></strong>.</p>
<p>So basically <em>Metro Bundler</em> works by taking your source code, all external library dependencies as well as static assets that you use and converting them, optimizing them, and packaging them into a single <em>bundle</em> that can be run by <code>React Native</code>.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-is-code-splitting">What is Code-Splitting?<a href="https://www.agas.dev/blog/repack#what-is-code-splitting" class="hash-link" aria-label="Direct link to What is Code-Splitting?" title="Direct link to What is Code-Splitting?" translate="no">​</a></h3>
<p>Code-Splitting is a technique that allows developers to create <strong>multiple bundle files</strong> from existing code sources, so the resulting bundle is not just one but consists of many files which are commonly referred to as <em>chunck</em>.</p>
<p>By default, all your input files (source code, dependencies, and assets) are combined into one file. With Code-Splitting, the bundle will be divided into several parts called <em>chunck</em>. The main <em>chunck</em> (also known as the <code>entry chunk</code>) is commonly referred to as the <em>main bundle</em>.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-do-you-need-repack-to-code-splitting">Why Do You Need Re.pack To Code-Splitting?<a href="https://www.agas.dev/blog/repack#why-do-you-need-repack-to-code-splitting" class="hash-link" aria-label="Direct link to Why Do You Need Re.pack To Code-Splitting?" title="Direct link to Why Do You Need Re.pack To Code-Splitting?" translate="no">​</a></h4>
<p>As explained above, by default React Native will use <code>Metro Bundler</code> to do JavaScript bundling. But unfortunately <em>Metro Bundler</em> cannot perform <em>Code-Splitting</em> technique by default. To be able to perform the <em>Code-Splitting</em> technique, we need a JavaScript bundler other than Metro Bundler.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-is-repack">What is Re.pack?<a href="https://www.agas.dev/blog/repack#what-is-repack" class="hash-link" aria-label="Direct link to What is Re.pack?" title="Direct link to What is Re.pack?" translate="no">​</a></h4>
<p><a href="https://github.com/callstack/repack" target="_blank" rel="noopener noreferrer" class="">Re.pack</a> is basically a toolkit that allows you to use Webpack and its <em>Code-Splitting</em> functionality and use them on React Native.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-do-we-need-code-splitting-in-react-native">Why Do We Need Code-Splitting in React Native?<a href="https://www.agas.dev/blog/repack#why-do-we-need-code-splitting-in-react-native" class="hash-link" aria-label="Direct link to Why Do We Need Code-Splitting in React Native?" title="Direct link to Why Do We Need Code-Splitting in React Native?" translate="no">​</a></h3>
<p>Single bundles aren't a bad thing, but as your app grows your bundle size will grow as well. Especially if you use a lot of third-party libraries you need to be very careful about the code that will be included in the application bundle so that you don't accidentally make your bundle so large that it takes a long time for React Native to load your application.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="increasing-app-startup-time">Increasing App Startup Time<a href="https://www.agas.dev/blog/repack#increasing-app-startup-time" class="hash-link" aria-label="Direct link to Increasing App Startup Time" title="Direct link to Increasing App Startup Time" translate="no">​</a></h4>
<p>If you have performance issues in the application startup area <em>Code-Splitting</em> is a technique worth trying when you have those problems.</p>
<p>Moving code from a single bundle to multiple chunks of <em>chunck</em> if properly configured can make your React Native app to only load the required code snippets and delay loading of other code on startup. This really helps improve the startup performance of your application.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="modular-applications">Modular Applications<a href="https://www.agas.dev/blog/repack#modular-applications" class="hash-link" aria-label="Direct link to Modular Applications" title="Direct link to Modular Applications" translate="no">​</a></h4>
<p>Apps that expose different functionality or different UI based on user details are examples of apps that would benefit greatly from Code-Splitting.</p>
<p>Let's take an example of an e-learning application like <em>Ruangguru</em>. With Code-Splitting, you will be able to separate student and teacher functionality in separate bundle files, so that the application only loads one of the bundles, this can improve startup performance by loading only code that is relevant to the user's needs.</p>
<p>Two other groups of apps where Code-Splitting plays a big role are super apps (like Gojek) as well as apps with mini app stores (like WeChat/Line). Instead of having multiple apps on the App Store and Google Play, you can combine them into one while streamlining development and simplifying management.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-to-use-repack-for-code-splitting-in-react-native">How to Use Re.pack for Code-Splitting in React Native<a href="https://www.agas.dev/blog/repack#how-to-use-repack-for-code-splitting-in-react-native" class="hash-link" aria-label="Direct link to How to Use Re.pack for Code-Splitting in React Native" title="Direct link to How to Use Re.pack for Code-Splitting in React Native" translate="no">​</a></h3>
<p>How to use Re.pack? The first step to using Re.pack is to install the required dependencies:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">npm i -D webpack terser-webpack-plugin babel-loader @callstack/repack</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># or</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">yarn add -D webpack terser-webpack-plugin babel-loader @callstack/repack</span><br></span></code></pre></div></div>
<p>After that, Create react-native.config.js (if this file is not in your project) and copy the content below:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">module</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token property-access">exports</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token literal-property property">commands</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">require</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'@callstack/repack/commands'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></span></code></pre></div></div>
<p>Then configure XCode and Gradle to use the webpack-bundle command. By making this change you will replace the default <code>Metro Bundler</code> to <code>Re.pack</code></p>
<ul>
<li class="">
<p>XCode: Add <code>export BUNDLE_COMMAND=webpack-bundle</code> to the <strong><em>Bundle React Native code and images</em></strong> phase of <strong><em>Build Phases</em></strong> in your XCode project configuration.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">export NODE_BINARY=node</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">export BUNDLE_COMMAND=webpack-bundle</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">../node_modules/react-native/scripts/react-native-xcode.sh</span><br></span></code></pre></div></div>
</li>
<li class="">
<p>Gradle: Add <code>bundleCommand: "webpack-bundle"</code> into <code>project.ext.react</code> in <code>android/app/build.gradle</code> file, so it looks similar to:</p>
<div class="language-groovy codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-groovy codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">project.ext.react = [</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    enableHermes: false,  // clean and rebuild if changing</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    bundleCommand: "webpack-bundle",</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    bundleInDebug: false</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">]</span><br></span></code></pre></div></div>
</li>
</ul>
<p>All configurations have been completed. Now you can use repack in your React Native application.</p>
<p>To run the development server you can use the command</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">npx react-native webpack-start</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-are-the-disadvantages-of-repack-compared-to-facebook-metro-bundler-">What are the disadvantages of Repack compared to Facebook Metro Bundler ?<a href="https://www.agas.dev/blog/repack#what-are-the-disadvantages-of-repack-compared-to-facebook-metro-bundler-" class="hash-link" aria-label="Direct link to What are the disadvantages of Repack compared to Facebook Metro Bundler ?" title="Direct link to What are the disadvantages of Repack compared to Facebook Metro Bundler ?" translate="no">​</a></h3>
<p>Of course there are some drawbacks when we use <em>Code-Splitting</em> with <em>Re.pack</em> instead of <em>Metro Bundler</em>. One of the biggest drawbacks is that we <strong>can't use <em>Codepush</em></strong> anymore to do <em>Hot Push</em> code in <em>Production Env</em>.</p>
<p>Besides that at the time this article was written if you use <em>Hermes</em> with <em>Re.pack</em> then it can only convert <em>Main Bundle</em> to <em>Hermes Bytecode</em>, chunck files outside of <em>Main Bundle</em> will not be transformed into <em>Hermes Bytecode</em>.</p>]]></content:encoded>
            <category>repack</category>
            <category>codesplit</category>
        </item>
    </channel>
</rss>