<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Posts on Tiziano Basile | Software Engineer</title><link>https://bstz.it/post/</link><description>Recent content in Posts on Tiziano Basile | Software Engineer</description><generator>Hugo -- gohugo.io</generator><language>en</language><copyright>Tiziano Basile</copyright><lastBuildDate>Thu, 12 Nov 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://bstz.it/post/index.xml" rel="self" type="application/rss+xml"/><item><title>Wild Shift Devlog #2: It's always the semicolon</title><link>https://bstz.it/p/wild-shift-devlog-2/</link><pubDate>Thu, 12 Nov 2026 00:00:00 +0000</pubDate><guid>https://bstz.it/p/wild-shift-devlog-2/</guid><description>&lt;p&gt;Second update on &lt;strong&gt;The Debugger 0x03: Wild Shift&lt;/strong&gt;, my GitHub Game Off 2026 entry (theme: &lt;strong&gt;SHIFT&lt;/strong&gt;). If you missed it, &lt;a class="link" href="https://bstz.it/p/wild-shift-devlog-1/" target="_blank" rel="noopener"
&gt;Devlog #1&lt;/a&gt; covered the register and the shift. This post is a mirror of the &lt;a class="link" href="https://basteez.itch.io/wild-shift/devlog/1771895/devlog-2-its-always-the-semicolon-infinite-loop-null-pointer-and-a-duck-that-talks" target="_blank" rel="noopener"
&gt;second devlog on itch.io&lt;/a&gt;. You can also play the current build in the browser on the &lt;a class="link" href="https://basteez.itch.io/wild-shift" target="_blank" rel="noopener"
&gt;game page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Two bosses down, one to go. Since the last devlog &lt;strong&gt;The Debugger 0x03: Wild Shift&lt;/strong&gt; got its first two real bugs to fix, a duck that talks, and two new bits for the register.&lt;/p&gt;
&lt;h2 id="infinite-loop-the-first-real-bug-epic-3"&gt;Infinite Loop: the first real bug (Epic 3)
&lt;/h2&gt;&lt;p&gt;Last time I promised a snake made of code. Here it is: &lt;strong&gt;Infinite Loop&lt;/strong&gt;, a ring of 24 &lt;code&gt;{ }&lt;/code&gt; segments biting its own tail, with an iteration counter &lt;code&gt;i = 0&lt;/code&gt; floating above it.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;It never stops.&lt;/strong&gt; The ring spins, drifts toward you and gets 5% faster on every lap. &lt;code&gt;i++&lt;/code&gt; ticks up as it goes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It&amp;rsquo;s always the semicolon.&lt;/strong&gt; The point where head meets tail is the loop condition, a &lt;code&gt;;&lt;/code&gt; that travels around the ring. Once per lap it opens for 1.5 seconds, and that&amp;rsquo;s the only place you can hurt it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Weak windows.&lt;/strong&gt; Every phase has an ordered pair of bits that &amp;ldquo;reproduces&amp;rdquo; the bug. Hit the boss with it and it&amp;rsquo;s &lt;strong&gt;Patched&lt;/strong&gt; for 3 seconds: your damage goes from ×0.5 (&lt;code&gt;// resisted&lt;/code&gt;) to ×1.5 (&lt;code&gt;CRITICAL&lt;/code&gt;, in caps, with a flash). Phase 1 wants &lt;code&gt;A→D&lt;/code&gt; (Dash Strike through the open &lt;code&gt;;&lt;/code&gt;), phase 2 wants &lt;code&gt;D→A&lt;/code&gt;, so you have to shift back and forth to patch and then cash in.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Three phases, four attacks.&lt;/strong&gt; &lt;strong&gt;Head Rush&lt;/strong&gt; (the head uncoils and charges), &lt;strong&gt;Constrict&lt;/strong&gt; (the ring re-centres on you and shrinks: dash &lt;em&gt;through&lt;/em&gt; the body to get out), and in phase 3 a &lt;strong&gt;Nested Loop&lt;/strong&gt;: it splits into an outer and an inner ring, each with its own &lt;code&gt;;&lt;/code&gt;, and they open in turn. Outer ring, &lt;code&gt;A→D&lt;/code&gt;. Inner ring, &lt;code&gt;D→A&lt;/code&gt;. Switching rings means switching windows. Plus a &lt;strong&gt;&lt;code&gt;;&lt;/code&gt; Spit&lt;/strong&gt; projectile, because of course.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://bstz.it/p/wild-shift-devlog-2/infinite-loop-ring-duck-bubble.png"
width="1280"
height="720"
srcset="https://bstz.it/p/wild-shift-devlog-2/infinite-loop-ring-duck-bubble_hu_3b33016cba9fc432.png 480w, https://bstz.it/p/wild-shift-devlog-2/infinite-loop-ring-duck-bubble_hu_eed89c16eb7125c6.png 1024w"
loading="lazy"
alt="Infinite Loop closing around the player"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
&gt;
&lt;em&gt;Infinite Loop in phase 2 (&lt;code&gt;i = 3&lt;/code&gt;) closing around the player. On the left, the duck is still nagging me to shift.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="the-duck-gets-a-voice"&gt;The duck gets a voice
&lt;/h3&gt;&lt;p&gt;The rubber duck now lives in a text bubble under the register and teaches the fight with jokes: &lt;em&gt;&amp;ldquo;See the semicolon? It&amp;rsquo;s always the semicolon. Hit it when it opens.&amp;rdquo;&lt;/em&gt; If you just stand there, it nudges you (&lt;em&gt;&amp;ldquo;Go on, shift. It&amp;rsquo;s waiting.&amp;rdquo;&lt;/em&gt;) and after a few seconds the loop starts anyway.&lt;/p&gt;
&lt;p&gt;The lines are typed out with a squeak and a gibberish voice made with &lt;a class="link" href="https://github.com/basteez/mumble-speech" target="_blank" rel="noopener"
&gt;&lt;strong&gt;mumble-speech&lt;/strong&gt;&lt;/a&gt;, a small library of mine that turns text into gibberish speech. Its v0.3.0 added a Godot web build, so the duck can talk in the browser too. No voice acting, just a very confident duck.&lt;/p&gt;
&lt;h3 id="defeat-and-harvest"&gt;Defeat and Harvest
&lt;/h3&gt;&lt;p&gt;When the bug dies, it collapses into the line of code it should have been: &lt;code&gt;for (i = 0; i &amp;lt; n; i++) { }&lt;/code&gt;. Then the &lt;strong&gt;Harvest&lt;/strong&gt; screen shows the bit it dropped and the new windows it opens in your ring.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://bstz.it/p/wild-shift-devlog-2/harvest-loop-bit.png"
width="1280"
height="720"
srcset="https://bstz.it/p/wild-shift-devlog-2/harvest-loop-bit_hu_99e02054569721e7.png 480w, https://bstz.it/p/wild-shift-devlog-2/harvest-loop-bit_hu_539e218a7837a8b9.png 1024w"
loading="lazy"
alt="Harvest screen after Infinite Loop"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
&gt;
&lt;em&gt;Harvest: Infinite Loop drops LOOP. The register becomes &lt;code&gt;[A, D, L] = 0x07&lt;/code&gt; and &lt;code&gt;D→L&lt;/code&gt; unlocks Echo Dash.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="loop-echo-dash-and-breakpoint-epic-4"&gt;LOOP, Echo Dash and Breakpoint (Epic 4)
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;LOOP (&lt;code&gt;L&lt;/code&gt;) = Echo.&lt;/strong&gt; It replays your last solo move from where you&amp;rsquo;re standing now, at 70% damage. Slash, step away, Echo: a second slash where you are. It has a 1.5 s cooldown, and if you haven&amp;rsquo;t done anything yet it just does nothing (no cooldown wasted).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Echo Dash (&lt;code&gt;D→L&lt;/code&gt;).&lt;/strong&gt; Dash, then Echo: a second dash you can point in a new direction, invulnerable the whole way through with no gap. It&amp;rsquo;s how you pass &lt;em&gt;through&lt;/em&gt; a boss, and against the next one it does more than that.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Breakpoint (&lt;code&gt;Space&lt;/code&gt;).&lt;/strong&gt; Freezes the simulation for up to 2 seconds, but shifting still works. The screen dithers, a red breakpoint dot appears, and you get to think. 12 s cooldown. &lt;em&gt;Finally, a debugger that works.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Duck hints.&lt;/strong&gt; If you&amp;rsquo;re stuck, the duck tells you the weak window: &lt;code&gt;&amp;quot;Quack. Weak window: D→A.&amp;quot;&lt;/code&gt;. It speaks on Breakpoint (once per phase) and every third death on the same boss. In the Nested Loop phase it names the window of the ring that opens next.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="null-pointer-a-ghost-that-points-at-nothing-epic-5"&gt;Null Pointer: a ghost that points at nothing (Epic 5)
&lt;/h2&gt;&lt;p&gt;The second bug is &lt;strong&gt;Null Pointer&lt;/strong&gt;, a ghost made of &lt;code&gt;null&lt;/code&gt; glyphs. It&amp;rsquo;s &lt;strong&gt;invisible&lt;/strong&gt;: you only see it while it attacks, for a few frames when you hit it, and for a second after an Echo Dash goes through it.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Finding it.&lt;/strong&gt; A column of falling &lt;code&gt;null&lt;/code&gt; glyphs drifts under it, and it hums, panned left or right by where it is. The sky comment reads &lt;code&gt;// FIXME: who owns this pointer?&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pointer Grab:&lt;/strong&gt; a &lt;code&gt;*&lt;/code&gt; appears behind you, then it grabs. &lt;strong&gt;Null Rain:&lt;/strong&gt; five marked areas, then &lt;code&gt;null&lt;/code&gt; falls on them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dangling Pointers&lt;/strong&gt; (phase 2): two decoy ghosts. They don&amp;rsquo;t hum, and hitting one just dispels it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NullPointerException.&lt;/strong&gt; Swing a slash at nothing while the ghost is close and you&amp;rsquo;re stunned for a third of a second. Don&amp;rsquo;t attack blindly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Watch (hold &lt;code&gt;R&lt;/code&gt;).&lt;/strong&gt; Reveals the ghost and its decoys, shows the name of its next attack, the bit it will drop and its weak window. You move at half speed and can&amp;rsquo;t act while you hold it, so it&amp;rsquo;s a peek, not a crutch.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://bstz.it/p/wild-shift-devlog-2/null-pointer-pointer-grab.png"
width="1280"
height="720"
srcset="https://bstz.it/p/wild-shift-devlog-2/null-pointer-pointer-grab_hu_b2f9da81dccff0ea.png 480w, https://bstz.it/p/wild-shift-devlog-2/null-pointer-pointer-grab_hu_fc271feb66918c8d.png 1024w"
loading="lazy"
alt="Pointer Grab tell"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
&gt;
&lt;em&gt;Pointer Grab&amp;rsquo;s tell: a &lt;code&gt;*&lt;/code&gt; lands right behind the player (&lt;code&gt;@&lt;/code&gt;) while the ghost closes in. &lt;code&gt;[D, L]&lt;/code&gt; is live, so Echo Dash is one K away.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://bstz.it/p/wild-shift-devlog-2/null-pointer-null-rain.png"
width="1280"
height="720"
srcset="https://bstz.it/p/wild-shift-devlog-2/null-pointer-null-rain_hu_38d24c3b79e0eaf3.png 480w, https://bstz.it/p/wild-shift-devlog-2/null-pointer-null-rain_hu_a3917d901bd261e5.png 1024w"
loading="lazy"
alt="Null Rain"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
&gt;
&lt;em&gt;Null Rain: five areas marked, and the column of &lt;code&gt;null&lt;/code&gt; glyphs that gives the invisible ghost away.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://bstz.it/p/wild-shift-devlog-2/null-pointer-watch.png"
width="1280"
height="720"
srcset="https://bstz.it/p/wild-shift-devlog-2/null-pointer-watch_hu_ddccb8d230e54ff2.png 480w, https://bstz.it/p/wild-shift-devlog-2/null-pointer-watch_hu_e69d781b81c25c78.png 1024w"
loading="lazy"
alt="Holding Watch on Null Pointer"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
&gt;
&lt;em&gt;Holding Watch: the ghost shows up, with its next attack (&lt;code&gt;pointer_grab&lt;/code&gt;), its drop (&lt;code&gt;N&lt;/code&gt;) and its weak window (&lt;code&gt;D→L&lt;/code&gt;).&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;At the start of the fight the duck introduces the two new skills (&lt;em&gt;&amp;ldquo;Hold R. Pointers point *somewhere*.&amp;rdquo;&lt;/em&gt;). Beat it and it collapses into &lt;code&gt;if (ptr != null) ptr.deref();&lt;/code&gt; and drops &lt;strong&gt;NULL&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Null Dodge (&lt;code&gt;N&lt;/code&gt;):&lt;/strong&gt; half a second of intangibility, at half speed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Clean Catch:&lt;/strong&gt; if an attack passes through you during a Null Dodge, your next damaging hit deals ×1.5, on top of Patched.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;(The &amp;ldquo;FIGHT N / boss index N&amp;rdquo; text in the middle of the arena is still a placeholder from the scene flow: it goes away in polish.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Fun fact: the GDD planned Epic 5 for November 18–21. It was done on November 12, so I&amp;rsquo;m almost a week ahead of schedule.&lt;/p&gt;
&lt;h2 id="housekeeping"&gt;Housekeeping
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;The game is live on itch.io as a web build (about 28 MB), with a deploy from CI. Running it in a real browser found two pause bugs (resume with &lt;code&gt;P&lt;/code&gt; or a click when fullscreen eats &lt;code&gt;Esc&lt;/code&gt;, and a game frozen after a focus-loss pause), both fixed.&lt;/li&gt;
&lt;li&gt;The web build budget went from 30 to 32 MB to make room for the voice and the second boss.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="how-its-made"&gt;How it&amp;rsquo;s made
&lt;/h2&gt;&lt;p&gt;Same as before: I&amp;rsquo;m the director and the playtester, AI agents (Claude) write the code from specs I approve with the &lt;a class="link" href="https://github.com/bmad-code-org/BMAD-METHOD" target="_blank" rel="noopener"
&gt;BMAD Method&lt;/a&gt;, and every story is a pull request with code review and automated tests. The test suite went from 55 to &lt;strong&gt;95 test files&lt;/strong&gt;. Everything is public in the &lt;a class="link" href="https://github.com/basteez/the-debugger-0x3-wild-shift" target="_blank" rel="noopener"
&gt;GitHub repo&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="next-up"&gt;Next up
&lt;/h2&gt;&lt;p&gt;One bug left, and it&amp;rsquo;s the worst kind. It reads and writes memory it shouldn&amp;rsquo;t, and it doesn&amp;rsquo;t care that the register is &lt;em&gt;yours&lt;/em&gt;. Plus an intro and an ending, so the run finally has a beginning and a &lt;code&gt;return&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;</description></item><item><title>Wild Shift Devlog #1: Your weapon is a byte</title><link>https://bstz.it/p/wild-shift-devlog-1/</link><pubDate>Tue, 10 Nov 2026 00:00:00 +0000</pubDate><guid>https://bstz.it/p/wild-shift-devlog-1/</guid><description>&lt;p&gt;It&amp;rsquo;s November again, which means GitHub Game Off. Last year I built &lt;a class="link" href="https://bstz.it/p/adding-interactive-cockpit-controls-to-grav-sync/" target="_blank" rel="noopener"
&gt;Grav-Sync&lt;/a&gt; in plain JavaScript; this year the theme is &lt;strong&gt;SHIFT&lt;/strong&gt; and my entry is &lt;strong&gt;The Debugger 0x03: Wild Shift&lt;/strong&gt;, a top-down boss rush made with Godot 4.7. You can follow it on &lt;a class="link" href="https://basteez.itch.io/wild-shift" target="_blank" rel="noopener"
&gt;itch.io&lt;/a&gt;, and this post is a mirror of the &lt;a class="link" href="https://basteez.itch.io/wild-shift/devlog/1767483/devlog-1-your-weapon-is-a-byte-building-the-register" target="_blank" rel="noopener"
&gt;first devlog I published there&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The idea in one line:&lt;/strong&gt; your weapon is a bit register. Shift it left or right mid-fight, and the two leftmost bits become your two action buttons. A present-day developer and their rubber duck get pulled into a fantasy RPG where the monsters are literal software bugs: Infinite Loop, Null Pointer and Segfault.&lt;/p&gt;
&lt;h2 id="day-1-making-it-run-in-the-browser-epic-1"&gt;Day 1: making it run in the browser (Epic 1)
&lt;/h2&gt;&lt;p&gt;Before writing any gameplay, I wanted the boring stuff to work: a Godot 4.7 project that runs at a locked 60 Hz with pixel-perfect integer scaling, a web export that stays under 30 MB, and a CI pipeline that runs the tests on every pull request.&lt;/p&gt;
&lt;p&gt;Then the shell of the game:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a &lt;strong&gt;title screen with &amp;ldquo;press to start&amp;rdquo;&lt;/strong&gt;, because browsers won&amp;rsquo;t play audio before your first input&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Clearing&lt;/strong&gt;, the arena, framed by an IDE-style HUD: register panel on the left, log panel at the bottom, line numbers down the edge&lt;/li&gt;
&lt;li&gt;the whole scene flow with placeholders (Title → Intro → Fight → Harvest → … → Ending)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;auto-pause&lt;/strong&gt; when you switch tabs, so the boss doesn&amp;rsquo;t kill you while you read Slack&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="days-23-the-shift-has-to-feel-good-epic-2"&gt;Days 2–3: the shift has to feel good (Epic 2)
&lt;/h2&gt;&lt;p&gt;The goal of Epic 2 was simple to say and hard to do: &lt;em&gt;shifting should feel great in an empty arena, before there&amp;rsquo;s anything to fight.&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The register is a ring.&lt;/strong&gt; &lt;code&gt;Q&lt;/code&gt; rotates it &lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt;, &lt;code&gt;E&lt;/code&gt; rotates it &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;. It only contains the bits you own, so you can never end up in a dead state. The HUD also shows its byte value, so your build is literally &lt;code&gt;0x03&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shift juice:&lt;/strong&gt; the register slides with a small overshoot bounce, a mechanical key-click (two different sounds for left and right), and your weapon inverts for 4 frames.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ATTACK and DASH:&lt;/strong&gt; a 3-hit slash chain and an 8-frame invulnerable dash.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ordered combos:&lt;/strong&gt; the order matters. &lt;code&gt;[A, D]&lt;/code&gt; (attack then dash) is &lt;strong&gt;Dash Strike&lt;/strong&gt;, &lt;code&gt;[D, A]&lt;/code&gt; is just a dash followed by a slash.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hit feedback:&lt;/strong&gt; hit-stop, glyph particles flying off the target, and every hit printed as a &lt;code&gt;console.log&lt;/code&gt; line.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://bstz.it/p/wild-shift-devlog-1/slash-chain.png"
width="1280"
height="720"
srcset="https://bstz.it/p/wild-shift-devlog-1/slash-chain_hu_46f6b28a07312bd6.png 480w, https://bstz.it/p/wild-shift-devlog-1/slash-chain_hu_9369637b36e4d3a4.png 1024w"
loading="lazy"
alt="The 3-hit slash chain on the practice target"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The 3-hit slash chain on the practice target: glyph particles and damage logged to the console.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://bstz.it/p/wild-shift-devlog-1/dash-strike.png"
width="1280"
height="720"
srcset="https://bstz.it/p/wild-shift-devlog-1/dash-strike_hu_8a2070696764be3.png 480w, https://bstz.it/p/wild-shift-devlog-1/dash-strike_hu_682d7451ef1c86a9.png 1024w"
loading="lazy"
alt="The register after one shift, with the Dash Strike window active"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;After one &lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt; the active window is &lt;code&gt;[A, D]&lt;/code&gt;: J→K fires a Dash Strike.&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Death and retry:&lt;/strong&gt; you have 5 HP, shown as &lt;code&gt;0b11111&lt;/code&gt;. When you die you get &lt;code&gt;Process exited with code 1&lt;/code&gt;, and one key puts you back at the start of the fight in under 2 seconds.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A practice target:&lt;/strong&gt; &lt;code&gt;/* test */&lt;/code&gt;, with infinite HP and a frame counter, so I can check the combo numbers on screen.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://bstz.it/p/wild-shift-devlog-1/process-exited.png"
width="1280"
height="720"
srcset="https://bstz.it/p/wild-shift-devlog-1/process-exited_hu_2b46cea53c299ce5.png 480w, https://bstz.it/p/wild-shift-devlog-1/process-exited_hu_d38d10e0974effc2.png 1024w"
loading="lazy"
alt="The death screen: Process exited with code 1"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
&gt;&lt;/p&gt;
&lt;p&gt;Under the hood the whole simulation is deterministic: it runs on a fixed 60 Hz clock, with seeded randomness and recorded input replays. That means a bug I find while playing can be replayed exactly in a test.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(The &amp;ldquo;FIGHT 1 / boss index 0&amp;rdquo; text in the screenshots is a placeholder: the arena is still waiting for its first boss.)&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="how-its-made"&gt;How it&amp;rsquo;s made
&lt;/h2&gt;&lt;p&gt;I&amp;rsquo;m making this solo, as director and playtester. The code is written by AI agents (Claude), following specs I write and approve with the &lt;a class="link" href="https://github.com/bmad-code-org/BMAD-METHOD" target="_blank" rel="noopener"
&gt;BMAD Method&lt;/a&gt;, the same workflow I described in &lt;a class="link" href="https://bstz.it/p/a-bmad-workflow-end-to-end-shipping-a-feature-the-spec-driven-way/" target="_blank" rel="noopener"
&gt;BMAD, end to end&lt;/a&gt;: game brief → GDD → architecture → one spec per story. Every story is a pull request with code review and automated tests (55 test files by the end of Epic 2). All the design docs are public in the &lt;a class="link" href="https://github.com/basteez/the-debugger-0x3-wild-shift" target="_blank" rel="noopener"
&gt;GitHub repo&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="next-up"&gt;Next up
&lt;/h2&gt;&lt;p&gt;The first real bug: &lt;strong&gt;Infinite Loop&lt;/strong&gt;, a snake made of code that repeats its attacks… until you find its weak window. Plus the rubber duck, who finally gets a voice, powered by &lt;a class="link" href="https://github.com/basteez/mumble-speech" target="_blank" rel="noopener"
&gt;mumble-speech&lt;/a&gt;, my own library: v0.3.0 added Godot web support, so the duck can talk in the browser too.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;</description></item><item><title>The NeuroDEVergent</title><link>https://bstz.it/p/the-neurodevergent/</link><pubDate>Sun, 11 Oct 2026 00:00:00 +0000</pubDate><guid>https://bstz.it/p/the-neurodevergent/</guid><description>&lt;p&gt;When I need to focus on something demanding I put my headphones on and start some thrash metal, at a volume any ENT doctor would probably advise against. I&amp;rsquo;ve tried white noise too, which is what everyone recommends, but it has never worked for me; thrash does, and it doesn&amp;rsquo;t even matter whether the track is instrumental or has vocals. My theory is that the tight, precise rhythms act as white noise in place of white noise, filling the space where my head would otherwise go looking for something else to do. If you want to try it, my suggestion is Metallica&amp;rsquo;s &lt;em&gt;&amp;hellip;And Justice for All&lt;/em&gt;, which is the album I put on when I need maximum focus, although, being a bass player, I usually listen to &lt;a class="link" href="https://www.youtube.com/watch?v=vumrar1k928" target="_blank" rel="noopener"
&gt;this version with the bass put back in the mix&lt;/a&gt;: I like the original, but I&amp;rsquo;d rather hear the bass.&lt;/p&gt;
&lt;p&gt;For years I took this as one of my fixations, one of the many things that have always made me feel a bit different from everyone else. Last week I was diagnosed with combined-type ADHD, along with giftedness, and to be honest I still haven&amp;rsquo;t quite worked out what that changes; all I know is that quite a few of those things now have an explanation slightly more serious than &amp;ldquo;that&amp;rsquo;s just how I am&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve felt different from other people since I was a kid although back then, at least on the inside, it didn&amp;rsquo;t weigh on me; if anything, I simply felt smarter, I had interests other kids my age didn&amp;rsquo;t have, like science, the human body and space, and I was into video games at a time, the second half of the &amp;rsquo;90s, when they weren&amp;rsquo;t yet the mainstream thing they are today. Above all I wanted to be a grown-up and that too has only found an explanation now because apparently that urge to be a grown-up was tied precisely to the giftedness.&lt;/p&gt;
&lt;p&gt;Growing up, though, that feeling of being different turned into something less harmless, and in my teens it became a feeling of being better than others. I&amp;rsquo;d already been bullied in primary school in a way I don&amp;rsquo;t want to talk about here and I was bullied again in middle school, where it was mostly psychological; to this day I couldn&amp;rsquo;t say how much of it came from that sense of superiority and how much from the fact that I really was different, probably a bit of both.&lt;/p&gt;
&lt;p&gt;Then came the inability to make friends or at least to have social interactions that actually fulfilled me; the few friendships I&amp;rsquo;d managed to build I lost anyway because I got bored quickly of people as much as of situations and I&amp;rsquo;d end up distancing myself by behaving like a jerk.&lt;/p&gt;
&lt;p&gt;Then came the trouble concentrating: I&amp;rsquo;d drift off, procrastinate constantly, and I did it mostly with the activities that required the most mental effort, which, coming from someone who&amp;rsquo;s a developer for a living, sounds almost like a joke. It was while trying to understand what was wrong that I came across ADHD and at first I didn&amp;rsquo;t really see myself in it; only when I stopped watching TikTok and Instagram content and moved on to more serious sources I started to actually understand the subject: the books and videos of Dr Valerio Rosso and the work of various American psychologists and psychiatrists.&lt;/p&gt;
&lt;p&gt;In there I started recognising myself in quite a few things: first of all the difficulty concentrating and getting constantly distracted by low-value things, but also feeling drained by context switching, which happens to me even when I&amp;rsquo;m not getting distracted at all; then the difficulty with relationships, which I&amp;rsquo;ve already talked about.&lt;/p&gt;
&lt;p&gt;There were other things, though, that I never saw myself in: I don&amp;rsquo;t have the physical hyperactivity, the mental kind yes, and I&amp;rsquo;m not the type who loses things. Even the famous poor sense of time works differently in my case from how it&amp;rsquo;s usually described, because it&amp;rsquo;s not that I lose track of time during the day, it&amp;rsquo;s more of a long-term thing: I&amp;rsquo;m 37 and I don&amp;rsquo;t realise it, I feel much younger, as if I still had all the time in the world ahead of me.&lt;/p&gt;
&lt;p&gt;About four years ago I had a first assessment at the public mental health centre in my city, with one of the psychiatrists there. I wasn&amp;rsquo;t given any specific test, and four things were enough to rule out ADHD: I did well at school, I&amp;rsquo;d taught myself to play bass, I&amp;rsquo;d taught myself English, and I&amp;rsquo;m in a long-term relationship with my partner. At the time I was puzzled, then I let it go and for a while I didn&amp;rsquo;t think about it any more. In hindsight, nearly all the things I&amp;rsquo;d been given as proof that I couldn&amp;rsquo;t have ADHD are exactly the ones giftedness makes possible, and which had therefore ended up hiding it.&lt;/p&gt;
&lt;p&gt;The nagging doubt came back this year, in June, during a more exhausting period than usual, both for concentration and for organising my life, which is something I&amp;rsquo;ve always sucked at. This time I looked for a serious professional and found one almost by chance, among ADHD podcasts: Dr Stefano Bramante, a psychiatrist and the man behind the &lt;em&gt;Brain on Fire&lt;/em&gt; podcast, who has also published a mock ADHD assessment on YouTube. He first referred me to one of his collaborators, Dr Daniele Tatano, a psychologist, who did an initial assessment that Dr Bramante then validated with his own; because of their availability, though, I had to wait until September.&lt;/p&gt;
&lt;p&gt;It was a completely different experience from four years earlier. Over six weeks I had two sessions with Dr Tatano and one with Dr Bramante, cognitive tests, questionnaires to fill in myself and others for my partner and my mother to fill in, interviews, and they even wanted to see my primary and middle school reports. I liked the approach, structured and not superficial at all, but what surprised me most was seeing it confirmed in black and white, between the interviews and the school reports, that as a kid I really was very intelligent, and that the feeling of being different I talked about at the beginning wasn&amp;rsquo;t just in my head.&lt;/p&gt;
&lt;p&gt;Then there&amp;rsquo;s work, where all of this shows up in fairly recognisable ways, at least now that I know what to look for. I need new problems to stay engaged with a project, and my brain switches on with urgency: I tend to put off difficult tasks, but when the deadline gets close and the pressure rises, hyperfocus kicks in almost automatically, and the thing I&amp;rsquo;d been putting off for days becomes the only thing I can think about. Then there&amp;rsquo;s documentation, which I struggle to find my way through, and there are long meetings where I only play a marginal role, which are by far the most tiring for me. It&amp;rsquo;s not all bad, though: I work well under pressure, even if it doesn&amp;rsquo;t do me much good; I think I&amp;rsquo;m good at lateral thinking, and I&amp;rsquo;m obsessed with clean, well-written code, which in a job like mine is hardly a flaw.&lt;/p&gt;
&lt;p&gt;As for actual tools, apart from the thrash metal, I&amp;rsquo;ve never had many: I&amp;rsquo;ve always put events and appointments in Google Calendar, and since March I&amp;rsquo;ve been using a bullet journal. It&amp;rsquo;s not the first one I&amp;rsquo;ve started, I abandoned all the others, but this one is still going after seven months, and for me that&amp;rsquo;s already something. At first I mostly used it for tasks, work and otherwise, then I didn&amp;rsquo;t want to keep track of work by hand any more and did what for an engineer is almost an occupational hazard: I automated it.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve instructed Claude to log every action, analysis, PR or comment I make or am asked for in my Logseq journal, with links to the PRs and comments, both on GitHub and in the issue tracker. The result is that I can now give much more structured feedback on my day-to-day work, and reconstructing what I did days or weeks later has become much easier. These days I use the bullet journal almost only for to-dos, and I&amp;rsquo;ve started, timidly, to push myself to write down a few thoughts in it too, a few emotions, that kind of thing.&lt;/p&gt;
&lt;p&gt;If you ask me what I felt when I got the diagnosis, the honest answer is that I don&amp;rsquo;t know, and as I said at the beginning I&amp;rsquo;m not even sure what changes from here on. I know I&amp;rsquo;m planning to start therapy, partly to work on organising my life, which I really want to get better at.&lt;/p&gt;
&lt;p&gt;And I know one more thing, which is maybe the only thing I&amp;rsquo;ve been clear about from the very first moment: I don&amp;rsquo;t want to use ADHD as an excuse, either for not doing things or for not organising my life.&lt;/p&gt;</description></item><item><title>Yes, you still need to know how to code</title><link>https://bstz.it/p/yes-you-still-need-to-know-how-to-code/</link><pubDate>Mon, 14 Sep 2026 00:00:00 +0000</pubDate><guid>https://bstz.it/p/yes-you-still-need-to-know-how-to-code/</guid><description>&lt;p&gt;AI writes good code, faster than me most of the time and usually cleaner than my first pass. I&amp;rsquo;m not going to argue otherwise and I&amp;rsquo;m not interested in the version of this piece where the craft is sacred and typing things by hand builds character.&lt;/p&gt;
&lt;p&gt;What I think is that the hard part of the job was never the typing, it was the part nobody watches: working out whether the thing is correct, whether it will hold, whether it should exist at all, and that part hasn&amp;rsquo;t got cheaper.&lt;/p&gt;
&lt;p&gt;Actually, &amp;ldquo;hasn&amp;rsquo;t got cheaper&amp;rdquo; is too soft. Reading is cheap when you can trust the author&amp;rsquo;s intent and expensive when you can&amp;rsquo;t, and a model hands you volume with no stable intent to anchor to, so the careful read, the kind that catches the subtle thing, is exactly the one that gets expensive, and exactly the one people skip. The cost didn&amp;rsquo;t vanish when generation got cheap, it moved to the other side of the ledger where it&amp;rsquo;s harder to see and much easier to skip.&lt;/p&gt;
&lt;p&gt;The reason it&amp;rsquo;s easy to skip is that the output is optimised to look right, not to be right. A model trained to produce the most likely continuation leans, over and over, in the same direction: toward the plausible. A developer who can&amp;rsquo;t read code doesn&amp;rsquo;t avoid that cost, they defer it into debt and into bugs they aren&amp;rsquo;t equipped to notice.&lt;/p&gt;
&lt;h2 id="a-prompt-is-not-a-task"&gt;A prompt is not a task
&lt;/h2&gt;&lt;p&gt;&lt;em&gt;Write me a function that does X&lt;/em&gt; looks like handing off a chore but it isn&amp;rsquo;t. Buried in it are decisions about structure, trade-offs, failure behaviour, and the quiet one underneath: what &amp;ldquo;correct&amp;rdquo; means in this particular domain. And if you can read the output, you can see those decisions and keep them. You notice that it reached for an ORM where a query would have done, that it picked a pattern that fights a constraint it doesn&amp;rsquo;t know about, that it decided errors should be swallowed instead of raised. Some of those you&amp;rsquo;ll accept, that&amp;rsquo;s fine; the point is that you accepted them. If you can&amp;rsquo;t read the output, you&amp;rsquo;re not delegating how something gets done, you&amp;rsquo;re accepting what gets done, in bulk, and you won&amp;rsquo;t notice the difference until much later.&lt;/p&gt;
&lt;h2 id="where-plausible-actually-bites"&gt;Where plausible actually bites
&lt;/h2&gt;&lt;p&gt;The clearest case is debugging, because there the gap shows up as a test going green. A green test never could tell you whether the cause was gone or just hidden, that&amp;rsquo;s not new; what&amp;rsquo;s new is the speed and the fluency, the model reaches for the change that silences the symptom, hands it back looking finished, and does it fast enough that the pause where you&amp;rsquo;d have got suspicious never happens.&lt;/p&gt;
&lt;p&gt;Review has a version of the same problem, except worse, because the dangerous code isn&amp;rsquo;t the code with obvious bugs in it, the model usually catches those. The dangerous code is locally fine and globally wrong: consistent with the file it lives in, in violation of something that lives three modules away, and the model isn&amp;rsquo;t holding a picture of your system, so if you aren&amp;rsquo;t either, you&amp;rsquo;re not reviewing anything.&lt;/p&gt;
&lt;p&gt;Security is where I&amp;rsquo;d be most nervous, because insecure code and secure code look the same to an untrained reader: the query gets parameterised in the path you were looking at and concatenated in the branch you weren&amp;rsquo;t, the permission check reads beautifully and still lets you fetch an object by guessing its ID, somebody logs a token &amp;ldquo;for debugging&amp;rdquo; and it survives into production. None of these fail a quick read, you catch them only if the threat model is already in your head.&lt;/p&gt;
&lt;p&gt;Architecture is a quieter failure: ask for a design and you get roughly the average of everything that&amp;rsquo;s been written about designs, which means the well-represented answer: the service boundaries, the fashionable abstraction, the thing that made sense for a company forty times your size. It has no idea you&amp;rsquo;re three people, or that the latency budget makes that choice unworkable, and it doesn&amp;rsquo;t volunteer &lt;em&gt;why not something simpler&lt;/em&gt;, which is the question that would have saved you.&lt;/p&gt;
&lt;p&gt;Then there&amp;rsquo;s the thing I don&amp;rsquo;t have a clean name for: the model rarely tells you this shouldn&amp;rsquo;t be built, it won&amp;rsquo;t volunteer that the problem is the wrong problem, it defaults to yes unless you build the friction that makes it say no. That kind of judgement lives in the things that don&amp;rsquo;t get built, and a system whose only output is the artefact can&amp;rsquo;t represent it at all.&lt;/p&gt;
&lt;p&gt;None of this means the only defence is a human squinting at a diff. Linters, type systems, scanners, a second model told to go looking for the bug: they catch a real share of what I&amp;rsquo;ve described, and you should use all of them, they raise the floor. But every one of them checks against a shape someone already knew to encode, so the failures that survive are the ones that don&amp;rsquo;t match a shape, and something still has to decide which of the machine&amp;rsquo;s findings matter and which it missed. That something is the picture of the system in your head.&lt;/p&gt;
&lt;h2 id="but-the-compiler"&gt;But the compiler
&lt;/h2&gt;&lt;p&gt;The obvious objection is that this is the assembly argument wearing new clothes: nobody reads assembly, the abstraction won, and complaining about it is just nostalgia with a technical vocabulary.&lt;/p&gt;
&lt;p&gt;I think the difference is real, and it&amp;rsquo;s the entire point. We stop looking underneath a layer, a compiler, a garbage collector, someone else&amp;rsquo;s merged pull request, when its failures are rare enough and cheap enough to catch downstream, not because it&amp;rsquo;s provably correct: the compiler earns that trust, and when it&amp;rsquo;s wrong, that&amp;rsquo;s a famous bug with a ticket number. An LLM hasn&amp;rsquo;t earned it, its failure surface is unbounded and shaped by the context you&amp;rsquo;re in, so the bet that mistakes will be rare and caught downstream doesn&amp;rsquo;t hold, and something you have to check on those terms is not an abstraction, it&amp;rsquo;s a collaborator, and to review a collaborator&amp;rsquo;s work at one level you need to understand the level below it.&lt;/p&gt;
&lt;p&gt;The second objection is that the models will get better and this is all temporary. I&amp;rsquo;m not going to bet against that, it&amp;rsquo;s a bad bet and I don&amp;rsquo;t need it, but better generation doesn&amp;rsquo;t dissolve the problem, it moves it up a level: the more capable the writer, the more you&amp;rsquo;re checking work that looks superhuman, and checking a collaborator you can&amp;rsquo;t out-reason still means understanding the level it works at, so verification doesn&amp;rsquo;t get free just because generation does. And underneath even flawless verification there&amp;rsquo;s a floor no model reaches for you: somebody has to say what to build, which trade-offs are acceptable, and what &amp;ldquo;correct&amp;rdquo; means for these users, and you can&amp;rsquo;t prompt your way to that if you don&amp;rsquo;t already know it. The better the tools get at the &lt;em&gt;how&lt;/em&gt;, the more everything depends on the &lt;em&gt;what&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="whats-actually-left"&gt;What&amp;rsquo;s actually left
&lt;/h2&gt;&lt;p&gt;Not syntax, not writing loops from memory: on that, the optimists are right, and it would be silly to defend it. When I say you still need to know how to code, I mean you need to be able to read it and reason about it, to hold what it&amp;rsquo;s doing in your head well enough to tell right from plausible; writing it was only ever the way most of us learned to do that.&lt;/p&gt;
&lt;p&gt;What&amp;rsquo;s left is the model in your head that lets you find your own ignorance: knowing where to look, which question collapses the thing, when green isn&amp;rsquo;t enough. That was never omniscience; I don&amp;rsquo;t understand every line I get back either, and I don&amp;rsquo;t understand every instruction my compiler emits, but the difference is that I know when I don&amp;rsquo;t understand something, and roughly where to point the torch.&lt;/p&gt;
&lt;p&gt;Though I&amp;rsquo;ll admit I can&amp;rsquo;t draw that line precisely, and I&amp;rsquo;m suspicious of anyone who says they can. I&amp;rsquo;ve stopped looking underneath the compiler entirely; I still look underneath the model, every time. Somewhere between those two there&amp;rsquo;s a threshold where checking stops being worth it, and I don&amp;rsquo;t know how you&amp;rsquo;d recognise it from the inside; you&amp;rsquo;d only find out afterwards, from the thing you failed to catch.&lt;/p&gt;
&lt;p&gt;None of this is about juniors, it applies to me at least as much: the tool has no idea how long you&amp;rsquo;ve been doing this, and leaning on it without the underlying model produces the same class of mistake regardless. But there&amp;rsquo;s an uncomfortable version I don&amp;rsquo;t want to skip: the model in your head got built by writing and debugging the very things the tool now does for you, and if you start out leaning on it, rationally, because it&amp;rsquo;s faster and cleaner than your first pass, you may never build the model at all, and I don&amp;rsquo;t have a tidy answer to that, I just don&amp;rsquo;t think the answer is to pretend the skill stopped mattering.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s the part worth keeping in mind: the tool multiplies whatever you bring, and it is extremely relaxed about what that is.&lt;/p&gt;</description></item><item><title>A BMAD workflow, end to end: shipping a feature the spec-driven way</title><link>https://bstz.it/p/a-bmad-workflow-end-to-end-shipping-a-feature-the-spec-driven-way/</link><pubDate>Thu, 06 Aug 2026 09:00:00 +0000</pubDate><guid>https://bstz.it/p/a-bmad-workflow-end-to-end-shipping-a-feature-the-spec-driven-way/</guid><description>&lt;p&gt;If the only way you&amp;rsquo;ve ever worked with an AI is to &lt;em&gt;vibe code&lt;/em&gt; with it, which is to say you open the editor, type a prompt, and keep whatever looks roughly right, then this is the post I wish someone had handed me back when I was doing exactly that. It isn&amp;rsquo;t another argument about why spec-driven development is better, because I already made that case &lt;a class="link" href="https://bstz.it/p/dont-be-mad-bmad-instead/" &gt;here&lt;/a&gt;; it&amp;rsquo;s simply one complete pass through a real workflow, with the actual commands, so that you can see for yourself what the whole thing looks like from the inside.&lt;/p&gt;
&lt;p&gt;Together we&amp;rsquo;ll build one small feature with &lt;a class="link" href="https://github.com/bmad-code-org/BMAD-METHOD" target="_blank" rel="noopener"
&gt;BMAD&lt;/a&gt;, taking it all the way from a vague idea to reviewed code, and although the example is deliberately thin it is also complete, running the full sequence of brainstorm → spec → architecture → story → implementation → review without waving our hands over any of the parts that usually get skipped, so that by the end you can see exactly where the friction lives and what it buys you in return.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve never met the BMAD agents before, the &lt;a class="link" href="https://bstz.it/p/bmad-meet-the-crew/" &gt;previous post&lt;/a&gt; introduces the crew, and while you don&amp;rsquo;t strictly need it in order to follow along here, it does help to know that &amp;ldquo;John&amp;rdquo; is a product manager and &amp;ldquo;Amelia&amp;rdquo; is a developer rather than people I actually work with.&lt;/p&gt;
&lt;p&gt;The example is a URL shortener where links can expire. All the artifacts you&amp;rsquo;ll see below are illustrative snippets from that walkthrough.&lt;/p&gt;
&lt;h2 id="the-feature-and-the-trap"&gt;The feature (and the trap)
&lt;/h2&gt;&lt;p&gt;Here&amp;rsquo;s the feature: &lt;strong&gt;a URL shortener where links can expire.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Now watch how a vibe-coding session usually starts, because you&amp;rsquo;d open a chat and type something like this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Build me a URL shortener in [your stack]. Links should be able to expire.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That prompt &lt;em&gt;feels&lt;/em&gt; complete, but it really isn&amp;rsquo;t, because it&amp;rsquo;s a specification riddled with holes, and the AI is going to quietly fill every one of those holes with a guess that you never actually got to see it make, so it&amp;rsquo;s worth counting them:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;When does a link expire?&lt;/strong&gt; After a fixed TTL? At an absolute timestamp you pass in? After N clicks? &amp;ldquo;Expire&amp;rdquo; is three different features.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;What happens when someone hits an expired link?&lt;/strong&gt; A &lt;code&gt;404&lt;/code&gt;? A &lt;code&gt;410 Gone&lt;/code&gt;? A redirect to a &amp;ldquo;this link expired&amp;rdquo; page? These are &lt;em&gt;observably different&lt;/em&gt; behaviors, and a caller integrating with you will build against whichever one you pick.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Do we count clicks?&lt;/strong&gt; If expiry can be click-based, you need a counter. If it can&amp;rsquo;t, you probably still want analytics. That&amp;rsquo;s a storage decision hiding inside a one-line prompt.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;What&amp;rsquo;s the ID scheme?&lt;/strong&gt; Random short codes? Collisions? Custom aliases?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Vibe coding doesn&amp;rsquo;t make any of these decisions go away, it simply arranges for &lt;em&gt;someone else&lt;/em&gt; to make them silently and invisibly at generation time, and then it hands you back a pile of code that you now have to reverse-engineer in order to discover what was actually decided on your behalf. So when the &amp;ldquo;expired link returns 404&amp;rdquo; bug report lands on your desk three weeks later, you&amp;rsquo;ll find yourself debugging a decision that nobody ever consciously made in the first place.&lt;/p&gt;
&lt;p&gt;Spec-driven development, at its core, is really just this: you &lt;strong&gt;make the decisions before you write the code, on purpose, and in a place where you can actually see them&lt;/strong&gt;, and BMAD is simply one way of running that process with the AI working inside the loop with you rather than against you.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s run it.&lt;/p&gt;
&lt;h2 id="the-mental-model-in-30-seconds"&gt;The mental model, in 30 seconds
&lt;/h2&gt;&lt;p&gt;Two things to hold in your head before we start:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;BMAD is a team of agents rather than a single assistant.&lt;/strong&gt; Each one is a persona with a specific job to do, so there&amp;rsquo;s an analyst who interrogates the idea, a product manager who writes the requirements, an architect who makes the technical calls and a developer who implements them, and you move between these personas deliberately as the work progresses. In the commands that follow, every phase can be run either by summoning the persona directly with &lt;code&gt;/bmad-agent-*&lt;/code&gt; or by invoking its underlying workflow with &lt;code&gt;/bmad-*&lt;/code&gt;, and you should feel free to use whichever of the two you prefer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The real output of the early phases is not code at all, it&amp;rsquo;s artifacts.&lt;/strong&gt; These are things like a brief, a PRD, an architecture document and a story file, all of them plain markdown that lives right there in your repository, and the code itself only arrives &lt;em&gt;last&lt;/em&gt;, sitting downstream of a set of documents that you can read, review and version like anything else.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Broadly speaking there are two stages to all of this, a &lt;strong&gt;planning&lt;/strong&gt; stage in which you work out what to build and how to build it, followed by an &lt;strong&gt;implementation&lt;/strong&gt; stage in which you actually build it, and the whole point of the arrangement is that planning leaves behind durable, reviewable artifacts which then carry all of their context forward into implementation, so that the AI writing your code is working from a genuine spec rather than from a vibe.&lt;/p&gt;
&lt;figure class="mermaid-figure"&gt;
&lt;pre class="mermaid"&gt;flowchart LR
idea([Vague idea]) --&amp;gt; A
subgraph Planning
A[&amp;#34;🔍 Analyst (Mary)&amp;lt;br/&amp;gt;brainstorm&amp;#34;] --&amp;gt; P[&amp;#34;📋 PM (John)&amp;lt;br/&amp;gt;PRD / spec&amp;#34;]
P --&amp;gt; W[&amp;#34;🏛️ Architect (Winston)&amp;lt;br/&amp;gt;architecture&amp;#34;]
W --&amp;gt; S[&amp;#34;📝 Story&amp;lt;br/&amp;gt;context handoff&amp;#34;]
end
subgraph Implementation
S --&amp;gt; D[&amp;#34;💻 Dev (Amelia)&amp;lt;br/&amp;gt;implement&amp;#34;]
D --&amp;gt; R[&amp;#34;🔎 Code review&amp;#34;]
end
R --&amp;gt; code([Reviewed code])
A -.-&amp;gt; brief[/brief.md/]
P -.-&amp;gt; prd[/prd.md/]
W -.-&amp;gt; arch[/architecture.md/]
S -.-&amp;gt; story[/story.md/]&lt;/pre&gt;
&lt;figcaption class="mermaid-caption"&gt;🔍 click the diagram to enlarge&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Everything drawn with a dotted line there is a file that ends up in your repository, and that is precisely the part of the process that vibe coding never produces.&lt;/p&gt;
&lt;h2 id="phase-1-brainstorm-drag-the-hidden-decisions-into-the-light"&gt;Phase 1, Brainstorm: drag the hidden decisions into the light
&lt;/h2&gt;&lt;p&gt;We start with Mary, the analyst, and a brainstorming session:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/bmad-agent-analyst # then pick the brainstorming capability
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# or go straight to it:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/bmad-brainstorming
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The move here is genuinely counterintuitive if you&amp;rsquo;re accustomed to prompting an AI for output, because &lt;strong&gt;you&amp;rsquo;re not asking it for an answer at all, you&amp;rsquo;re asking it to interrogate you&lt;/strong&gt;, and Mary&amp;rsquo;s entire job in this session is to ask you the questions that you quietly skipped over. This is exactly the moment where those four hidden decisions from the trap section earlier stop being invisible and start being things you have to answer for.&lt;/p&gt;
&lt;p&gt;A brainstorm on &amp;ldquo;URL shortener with expiring links&amp;rdquo; should surface, at minimum: which &lt;em&gt;kinds&lt;/em&gt; of expiry we actually want (TTL vs absolute date vs click-count), the expired-link behavior as a deliberate choice, whether click tracking is a feature or just a mechanism, and the ID/alias scheme.&lt;/p&gt;
&lt;p&gt;The artifact that comes out of this phase is a &lt;strong&gt;brief&lt;/strong&gt;, and this really is the part worth staring at for a moment, because it doesn&amp;rsquo;t merely list the things we intend to build; it also records the things we have deliberately decided &lt;em&gt;not&lt;/em&gt; to build, so that those decisions leave a paper trail behind them instead of quietly evaporating. Here&amp;rsquo;s the heart of an example &lt;code&gt;brief.md&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;## Decisions (made on purpose, for v1)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; **Expiry model: absolute timestamp.** A link carries an optional &lt;span class="sb"&gt;`expires_at`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; (UTC instant). No &lt;span class="sb"&gt;`expires_at`&lt;/span&gt; means the link never expires.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; **Expiry is inclusive.** At exactly &lt;span class="sb"&gt;`expires_at`&lt;/span&gt;, the link is already expired.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; **Behavior on an expired link: &lt;span class="sb"&gt;`410 Gone`&lt;/span&gt;,** with body &lt;span class="sb"&gt;`{ &amp;#34;reason&amp;#34;: &amp;#34;expired&amp;#34; }`&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Not a &lt;span class="sb"&gt;`404`&lt;/span&gt;, not a &lt;span class="sb"&gt;`302`&lt;/span&gt; to a landing page.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; **ID scheme: 7-character base62 random code,** with collision-retry.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;## Explicitly out of scope for v1 (deferred, not forgotten)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; Relative / TTL expiry (&amp;#34;expire 24h after creation&amp;#34;).
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; Click-count expiry, click tracking / analytics.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; Custom aliases, custom &amp;#34;this link expired&amp;#34; landing pages.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Take a good look at that &amp;ldquo;out of scope&amp;rdquo; list, because every single line in it is a decision that a vibe-coding prompt would have made &lt;em&gt;for&lt;/em&gt; you, silently and without asking, whereas here every one of them is on the record, deferred deliberately rather than forgotten by accident.&lt;/p&gt;
&lt;p&gt;If you want a more formal artifact than a brainstorm dump:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/bmad-product-brief
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Notice what has already happened here, because we haven&amp;rsquo;t yet written a single line of code and we have already caught three or four decisions that vibe coding would otherwise have made for us essentially at random.&lt;/p&gt;
&lt;h2 id="phase-2-prd-turn-decisions-into-requirements-you-can-check"&gt;Phase 2, PRD: turn decisions into requirements you can check
&lt;/h2&gt;&lt;p&gt;Now we switch to John, the product manager, to produce a Product Requirements Document.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/bmad-agent-pm
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# or the workflow directly:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/bmad-prd
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;John&amp;rsquo;s job is to take something like &amp;ldquo;we decided that links expire by absolute timestamp and that expired links return &lt;code&gt;410 Gone&lt;/code&gt;&amp;rdquo; and convert it into requirements that are precise enough that you could hand them to a complete stranger and expect the right thing to come back, and this is the point at which the fuzzy brainstorm finally hardens into something resembling a contract.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s an example FR-3 from &lt;code&gt;prd.md&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;span class="lnt"&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;### FR-3: Accessing an expired link
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; **Given** a short link whose &lt;span class="sb"&gt;`expires_at`&lt;/span&gt; is at or before now
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; **When** a client requests &lt;span class="sb"&gt;`GET /{code}`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; **Then** the service responds &lt;span class="sb"&gt;`410 Gone`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; **And** the response body includes a machine-readable &lt;span class="sb"&gt;`reason: &amp;#34;expired&amp;#34;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; **And** no redirect (&lt;span class="sb"&gt;`3xx`&lt;/span&gt;) is issued
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Non-goals for v1: custom &amp;#34;expired&amp;#34; landing pages, per-link grace periods.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Read that through and notice what has changed, because the &lt;code&gt;410&lt;/code&gt; decision is now &lt;strong&gt;written down, reviewable and testable&lt;/strong&gt;, which means that a month from now the question &amp;ldquo;why does it return 410?&amp;rdquo; has an actual answer with a paper trail attached to it, and that really is the whole difference between a decision and an accident.&lt;/p&gt;
&lt;p&gt;If you want the requirements distilled into a tighter, machine-oriented contract for the downstream agents, BMAD has a dedicated step:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/bmad-spec
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="phase-3-architecture-the-technical-calls-made-once"&gt;Phase 3, Architecture: the technical calls, made once
&lt;/h2&gt;&lt;p&gt;Winston, the architect, takes the &lt;em&gt;what&lt;/em&gt; that we&amp;rsquo;ve established so far and decides the &lt;em&gt;how&lt;/em&gt;, and for a feature this small the right instinct is to keep the whole thing thin, because you really don&amp;rsquo;t need a forty-page architecture document for a URL shortener, you only need the handful of decisions that would be genuinely expensive to get wrong:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/bmad-agent-architect
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# or:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/bmad-architecture
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The decisions worth pinning down here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Storage:&lt;/strong&gt; how do we persist &lt;code&gt;code → target_url + expires_at&lt;/code&gt;? Key-value store, relational table, in-memory for a toy?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Expiry enforcement:&lt;/strong&gt; lazy (check &lt;code&gt;expires_at&lt;/code&gt; on read) or active (a sweeper that deletes expired rows)?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ID generation:&lt;/strong&gt; how we mint short codes and handle collisions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The lazy-versus-active call is the one with real teeth in it, and here is how &lt;code&gt;architecture.md&lt;/code&gt; records that call, capturing both the decision &lt;em&gt;and&lt;/em&gt; its consequence:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;### Expiry enforcement: lazy (checked on read)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; **Chosen: lazy.** Simplest correct behavior; no scheduler, no clock daemon.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; **Consequence, on purpose:** an expired link&amp;#39;s record still &lt;span class="ge"&gt;_exists_&lt;/span&gt; in the
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; store until something evicts it. &lt;span class="sb"&gt;`resolve`&lt;/span&gt; must therefore never assume
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;present in store&amp;#34; means &amp;#34;live&amp;#34;. It must evaluate expiry every time.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; Rejected for v1: active sweeping. Adds a scheduler we don&amp;#39;t need yet.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Writing down the &lt;em&gt;consequence&lt;/em&gt;, namely that &amp;ldquo;present in store&amp;rdquo; does not mean &amp;ldquo;live&amp;rdquo;, is the part that pays off later on, because it&amp;rsquo;s the exact invariant that the implementation is going to have to honor, and it has been stated in plain language before a single line of &lt;code&gt;resolve&lt;/code&gt; even exists. The output of all this is an &lt;code&gt;architecture.md&lt;/code&gt; sitting in your repository, reviewable and versioned, the kind of thing a teammate can simply read instead of having to reverse-engineer it back out of the code.&lt;/p&gt;
&lt;h2 id="phase-4-the-story-packaging-context-for-the-implementer"&gt;Phase 4, The story: packaging context for the implementer
&lt;/h2&gt;&lt;p&gt;This is the phase that vibe coders have quite simply never seen, and it also happens to be the one that makes the whole method actually work.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/bmad-create-epics-and-stories # break the PRD into epics + stories
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/bmad-create-story # generate the next story with full context
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;A &lt;strong&gt;story&lt;/strong&gt; is a single, self-contained unit of work, but the genuinely important part is &lt;em&gt;what&amp;rsquo;s actually in it&lt;/em&gt;, because a BMAD story file is nothing like a Jira ticket that carries a title and a single sentence of description. It is instead a complete context package, gathering together the relevant requirements, the architectural decisions that happen to apply, the acceptance criteria and pointers to the exact files that are in play, and it is written in such a way that an agent, or for that matter a human, can pick it up completely cold and still have everything they need to get started.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s an example &lt;code&gt;story-1.3&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;# Story 1.3 — Return 410 for expired links
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;## Context
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Implements FR-3 (see prd.md). Expiry is enforced lazily on read, and the
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;boundary instant is inclusive (now &amp;gt;= expires_at ⇒ expired) — both are
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;architecture decisions. &lt;span class="sb"&gt;`now`&lt;/span&gt; is injected into resolve() so expiry stays
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;deterministic in tests.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;## Acceptance criteria
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;- [ ]&lt;/span&gt; resolve of a link whose expires_at &amp;lt;= now → 410, body {&amp;#34;reason&amp;#34;: &amp;#34;expired&amp;#34;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;- [ ]&lt;/span&gt; no 3xx is produced for an expired link
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;- [ ]&lt;/span&gt; a link with expires_at in the future still redirects 302 (regression guard)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;- [ ]&lt;/span&gt; a link with expires_at is None still redirects 302 (never expires)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;- [ ]&lt;/span&gt; at exactly expires_at, the link is already expired (boundary is inclusive)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;- [ ]&lt;/span&gt; unknown code still returns 404 (unchanged)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;## Files in play
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; shortener/service.py (add &lt;span class="sb"&gt;`now`&lt;/span&gt; param to resolve; add expiry branch)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; shortener/http_app.py (supply now = datetime.now(timezone.utc))
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; tests/test_service.py (expiry + regression cases)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Notice how the criteria carry all of the upstream decisions through verbatim, including the inclusive boundary, the &amp;ldquo;no 3xx&amp;rdquo; rule and the regression guards, so that nothing at all has been left open to interpretation.&lt;/p&gt;
&lt;p&gt;Why does any of this matter so much? It matters because when you hand &lt;em&gt;this&lt;/em&gt; to an implementer there is genuinely nothing left for them to guess at, since the &lt;code&gt;410&lt;/code&gt; is no longer a coin flip but an acceptance criterion with a regression guard sitting right next to it, and the story turns out to be the vehicle that carries every last decision you made back in phases 1 through 3 forward into the code itself.&lt;/p&gt;
&lt;p&gt;With the story now in hand, all of the planning artifacts are finally in place, and before writing any code at all it is worth taking a moment to check that they actually line up with one another:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/bmad-check-implementation-readiness
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This validates that your PRD, your architecture and your stories all agree with one another before anyone starts implementing anything, which is really the spec-driven equivalent of the old carpenter&amp;rsquo;s rule to measure twice and cut once.&lt;/p&gt;
&lt;h2 id="phase-5-implementation-now-finally-code"&gt;Phase 5, Implementation: now, finally, code
&lt;/h2&gt;&lt;p&gt;Only now do we finally sit down to write code, and by this point the act of &amp;ldquo;writing the code&amp;rdquo; has become almost mechanical, precisely because every meaningful decision was already made further upstream.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/bmad-agent-dev
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# or run a story end to end:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/bmad-dev-story
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Amelia, the developer, works &lt;em&gt;from the story&lt;/em&gt;, implementing against the acceptance criteria one by one, and the real tell that you are no longer vibe coding is that she can check her own work against a written definition of done rather than against her own intuition. Whenever she finds herself uncertain about something, the answer is already sitting in the story, or in the PRD, or in the architecture document, and never in a guess.&lt;/p&gt;
&lt;p&gt;The entire implementation of story 1.3 is this diff to &lt;code&gt;resolve&lt;/code&gt;, and because the story pre-decided everything the change is almost mechanical:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-diff" data-lang="diff"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;-def resolve(store: InMemoryStore, code: str) -&amp;gt; Resolution:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- &amp;#34;&amp;#34;&amp;#34;Resolve a short code to a redirect or an error (FR-2, FR-4).&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;&lt;/span&gt;&lt;span class="gi"&gt;+def resolve(store: InMemoryStore, code: str, *, now: datetime) -&amp;gt; Resolution:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ &amp;#34;&amp;#34;&amp;#34;Resolve a short code to a redirect or an error (FR-2, FR-3, FR-4).
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ Expiry is evaluated lazily on every read: a record present in the store is
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ not necessarily live. The boundary is inclusive — at exactly ``expires_at``
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ the link is already gone.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ &amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;&lt;/span&gt; link = store.get(code)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; if link is None:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; return Resolution(status=404, body={&amp;#34;reason&amp;#34;: &amp;#34;not_found&amp;#34;})
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ if link.expires_at is not None and link.expires_at &amp;lt;= now:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ return Resolution(status=410, body={&amp;#34;reason&amp;#34;: &amp;#34;expired&amp;#34;})
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;&lt;/span&gt; return Resolution(status=302, location=link.target_url)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;It comes down to two lines of logic, but the part worth looking at is the docstring, because it cites FR-3, the lazy-expiry decision and the inclusive boundary all together, and it can do so only because all three of them came &lt;em&gt;down the pipe&lt;/em&gt; from the artifacts that preceded it. The code, in other words, is not the place where any of those decisions were made, it is simply the place where they were finally applied.&lt;/p&gt;
&lt;p&gt;Contrast this with the ordinary vibe-coding loop, where you prompt the model, eyeball whatever output comes back and then re-prompt it whenever the result is wrong, because here the loop instead becomes implement against the criteria, verify against the criteria and you&amp;rsquo;re done. The AI is of course still doing all of the actual typing, but this time it is typing toward a target that you defined in advance rather than toward some vague sense of &amp;ldquo;looks about right.&amp;rdquo;&lt;/p&gt;
&lt;h2 id="phase-6-review-verify-against-the-spec-not-against-vibes"&gt;Phase 6, Review: verify against the spec, not against vibes
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/bmad-code-review
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The review is what closes the loop, and here is the quiet superpower that comes from having done all of the upstream work first, which is that &lt;strong&gt;you can now review against the spec itself&lt;/strong&gt;. A question like &amp;ldquo;does the expired-link path return &lt;code&gt;410&lt;/code&gt; in the way that FR-3 requires?&amp;rdquo; has become a plain yes-or-no question, whereas without a spec to lean on, code review tends to degenerate into &amp;ldquo;looks fine to me&amp;rdquo;, which is really just vibe coding wearing a blazer.&lt;/p&gt;
&lt;p&gt;And this is exactly where the whole approach earned its keep, because the review caught a real defect, and that defect was only catchable in the first place &lt;em&gt;precisely because there was a spec to check the code against&lt;/em&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Finding:&lt;/strong&gt; &lt;code&gt;architecture.md&lt;/code&gt; states an invariant — &amp;ldquo;timestamps are UTC-aware; naive datetimes are a caller error&amp;rdquo; — but nothing enforces it. &lt;code&gt;create_link&lt;/code&gt; happily accepts a naive &lt;code&gt;expires_at&lt;/code&gt;. At resolve time, &lt;code&gt;expires_at &amp;lt;= now&lt;/code&gt; then compares a naive datetime against the adapter&amp;rsquo;s aware &lt;code&gt;now&lt;/code&gt; and raises &lt;code&gt;TypeError: can't compare offset-naive and offset-aware datetimes&lt;/code&gt;. FR-3 promised a &lt;code&gt;410&lt;/code&gt;; the caller gets an unhandled &lt;code&gt;500&lt;/code&gt; instead.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That single bug is the whole argument for spec-driven development compressed into one example, because a vibe-coded shortener would almost certainly have carried &lt;em&gt;the very same defect&lt;/em&gt;, and yet with no &lt;code&gt;architecture.md&lt;/code&gt; invariant for it to violate, the reviewer would have had nothing to notice it against in the first place. The spec did more than merely document the decision, it turned what would have been a silent 500 into a visible finding, and the fix itself came down to a three-line guard in &lt;code&gt;create_link&lt;/code&gt; that fails fast with a clear error, together with a regression test, and because the invariant had been written down all along, the question &amp;ldquo;is this actually fixed?&amp;rdquo; is once again a simple yes-or-no.&lt;/p&gt;
&lt;h2 id="what-actually-just-happened"&gt;What actually just happened
&lt;/h2&gt;&lt;p&gt;Step back and look at what we&amp;rsquo;ve produced:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;brief.md&lt;/code&gt;: the ideas and the decisions we deferred&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prd.md&lt;/code&gt;: the requirements, testable&lt;/li&gt;
&lt;li&gt;&lt;code&gt;architecture.md&lt;/code&gt;: the technical calls&lt;/li&gt;
&lt;li&gt;&lt;code&gt;story-1.3.md&lt;/code&gt;: the context package that drove the code&lt;/li&gt;
&lt;li&gt;the code itself, plus tests&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The code is the &lt;em&gt;smallest&lt;/em&gt; artifact in that entire list, and it is also the very last one we made, and that inversion is really the whole point of the exercise. In vibe coding the code is the first and only artifact you ever produce, so every decision ends up trapped inside it and left undocumented, whereas in spec-driven development the code is instead a &lt;em&gt;consequence&lt;/em&gt; of everything else, with the decisions themselves living in readable documents and the code standing as nothing more than their current implementation.&lt;/p&gt;
&lt;p&gt;This is exactly why the friction turns out to be worth it, and in a real sense the friction &lt;em&gt;is&lt;/em&gt; the value, because every question that Mary asked, every requirement that John pinned down and every call that Winston made are all decisions that would otherwise have been made silently by an autocomplete, at twice the speed and none of the visibility.&lt;/p&gt;
&lt;p&gt;You didn&amp;rsquo;t actually slow down at all, you simply moved the thinking to the front of the process where it happens to be cheap, instead of leaving it for the debugger later on where it is always expensive.&lt;/p&gt;
&lt;h2 id="try-it-on-something-small"&gt;Try it on something small
&lt;/h2&gt;&lt;p&gt;Don&amp;rsquo;t go and start with your production monolith. Pick a feature that is exactly this size, small enough that you can finish it in a single sitting and yet real enough that it still hides a few decisions inside it, and then run the full loop through once from beginning to end:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/bmad-brainstorming # surface the decisions
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/bmad-prd # write them down
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/bmad-architecture # make the technical calls
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/bmad-create-story # package the context
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/bmad-dev-story # implement against it
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/bmad-code-review # verify against the spec
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The first time you go through it, the whole process will feel like pure overhead, and you should do it anyway, because somewhere around the story phase, at the moment when you hand the implementer a spec and get back exactly what you asked for, it quietly stops feeling like overhead and starts feeling like the thing you had been missing all along.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s the moment the lever stops being a slot machine.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Want the backstory on why I stopped vibe coding? Start with &lt;a class="link" href="https://bstz.it/p/dont-be-mad-bmad-instead/" &gt;Don&amp;rsquo;t be mad, BMAD instead&lt;/a&gt;. Want to meet the agents properly? &lt;a class="link" href="https://bstz.it/p/bmad-meet-the-crew/" &gt;Here&amp;rsquo;s the crew&lt;/a&gt;. Want to see the method on a bigger project? &lt;a class="link" href="https://bstz.it/p/bmad-in-action-building-tododoro/" &gt;Building TODOdoro&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>The year I stopped writing code for fun</title><link>https://bstz.it/p/the-year-i-stopped-writing-code-for-fun/</link><pubDate>Fri, 31 Jul 2026 00:00:00 +0000</pubDate><guid>https://bstz.it/p/the-year-i-stopped-writing-code-for-fun/</guid><description>&lt;p&gt;Somewhere in the last six months I stopped opening my editor on a Sunday, and I don&amp;rsquo;t mean the work one, I mean the other one, the editor where the projects nobody asked for live.&lt;/p&gt;
&lt;p&gt;There was no burnout and no crisis, nothing you could point at and name, which is exactly why it took me so long to notice at all. Work was good and busy in the way that good work is busy, reviews went fine, features landed, and every metric that measures a software engineer from the outside was pointing up and to the right, so this isn&amp;rsquo;t a story about a job or a team or a workload, and on that front I&amp;rsquo;ve been lucky and I still am. Nothing was wrong, something was just gone.&lt;/p&gt;
&lt;h2 id="the-symptom-i-ignored"&gt;The symptom I ignored
&lt;/h2&gt;&lt;p&gt;Orgsidian (one of my side projects) sat untouched for months, which on its own means nothing, because side projects die all the time and dying is most of what side projects actually do. What was different this time is that I never decided to stop, because there was no evening where I sat in front of it and thought &amp;ldquo;this isn&amp;rsquo;t going anywhere&amp;rdquo;, I simply didn&amp;rsquo;t open it, first for a couple of weeks and then for a great deal longer than that.&lt;/p&gt;
&lt;p&gt;And when I finally did open it again, the first thing I felt wasn&amp;rsquo;t curiosity about where I&amp;rsquo;d left off, it was the urge to catch up, to drag the thing back into a state where it was worth having, which isn&amp;rsquo;t the same as wanting to build something and is really just wanting to close a gap.&lt;/p&gt;
&lt;p&gt;I told myself I was tired, and maybe it really was just a full stretch of life with more responsibilities than I used to have and less spare attention to spread around them, and reasonable explanations like that were available so I took them, because they were true enough to hold.&lt;/p&gt;
&lt;h2 id="shipping-more-enjoying-it-less"&gt;Shipping more, enjoying it less
&lt;/h2&gt;&lt;p&gt;I want to be careful here, because the lazy version of this story is a complaint about AI making the code worse, and &lt;strong&gt;that isn&amp;rsquo;t the story I have&lt;/strong&gt;. The code was fine, most of it is still running, and if you&amp;rsquo;d asked me during that year whether the tooling was worth it I&amp;rsquo;d have said obviously yes, and I&amp;rsquo;d have been right.&lt;/p&gt;
&lt;p&gt;What I remember about writing it is nothing. There&amp;rsquo;s no afternoon attached to any of it, no wrong turn I took first, no moment where it clicked, not one detail I could tell you now without going back to look it up, so it exists and my name is on the commits and it left no trace in me whatsoever.&lt;/p&gt;
&lt;p&gt;That kept happening, and it wasn&amp;rsquo;t only happening on the clock, it happened just as much on the things I was building for myself, which is the part that should have bothered me a lot sooner than it did.&lt;/p&gt;
&lt;h2 id="the-pleasure-was-in-the-session-not-the-artifact"&gt;The pleasure was in the session, not the artifact
&lt;/h2&gt;&lt;p&gt;Here&amp;rsquo;s what I think I had wrong the whole time: I assumed that what I liked was building things, objects, repos, features, deployed URLs with my name somewhere behind them. I don&amp;rsquo;t think that was ever really it. What I liked was the two hours.&lt;/p&gt;
&lt;p&gt;You know the ones I mean. You have a rough idea and no plan, the obvious approach turns out to be wrong in some interesting way, you end up reading the source of a dependency because the docs won&amp;rsquo;t tell you the one thing you actually need to know, and then you look up and it&amp;rsquo;s dark outside and you&amp;rsquo;ve got maybe twenty lines to show for the entire evening, and you feel great about it.&lt;/p&gt;
&lt;p&gt;AI is very good at compressing exactly that, and it&amp;rsquo;s supposed to be, because optimising for the artifact is the entire product and nobody is hiding it. But when the artifact turns up without the two hours attached to it, you end up with everything you said you wanted and none of the thing you were actually there for.&lt;/p&gt;
&lt;p&gt;I wrote about the other half of this a few months ago in &lt;a class="link" href="https://bstz.it/p/ai-and-the-craft-are-we-extending-ourselves-or-outsourcing-ourselves/" &gt;AI and the Craft&lt;/a&gt;, the half about what you trade away when you let the tool do the thinking for you, and that half is much easier to talk about, because saying you understand your own systems less sounds serious while saying you&amp;rsquo;re not having fun sounds like whining.&lt;/p&gt;
&lt;p&gt;It isn&amp;rsquo;t whining, though, it&amp;rsquo;s the reason I have this career in the first place. I didn&amp;rsquo;t arrive here through a plan, I arrived because at fifteen I found this more interesting than anything else on offer, and that kid is why I&amp;rsquo;m any good at the job now, and nobody was paying him to be interested. Whatever keeps him around seems worth maintaining on purpose, the same way you&amp;rsquo;d maintain anything else you depend on.&lt;/p&gt;
&lt;h2 id="so-im-starting-a-project-with-the-ai-switched-off"&gt;So I&amp;rsquo;m starting a project with the AI switched off
&lt;/h2&gt;&lt;p&gt;Not as a protest but as an experiment, with a single question sitting behind it, which is whether the thing I lost is actually gone or has just been crowded out.&lt;/p&gt;
&lt;p&gt;The rules are deliberately dumb, because clever rules turn into a system you end up maintaining instead of a project you actually build.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No chat and no agents. I&amp;rsquo;m not disabling autocompletion, because it&amp;rsquo;s genuinely useful and pretending I&amp;rsquo;d give it up would turn the whole thing into a performance, but what stops is opening the chat panel to do my thinking for me.&lt;/li&gt;
&lt;li&gt;Docs are fine, reading the source of my dependencies is better than fine and actively encouraged, and searching and forums are fine too.&lt;/li&gt;
&lt;li&gt;No deadline, no roadmap and no launch, because if it never ships the experiment has still run.&lt;/li&gt;
&lt;li&gt;One hour at a time, usually at the weekend, and &lt;strong&gt;the session is the unit&lt;/strong&gt;, not the milestone.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That last one is the whole point, because if the session is the unit then an hour spent reading with nothing committed at the end of it still counts, whereas under the old accounting that hour was simply wasted.&lt;/p&gt;
&lt;h2 id="what-i-expect-to-go-wrong"&gt;What I expect to go wrong
&lt;/h2&gt;&lt;p&gt;I haven&amp;rsquo;t started yet, and this is the last thing I&amp;rsquo;m writing before I do, so everything below is a guess, and guesses about your own future behaviour are worth roughly what you&amp;rsquo;d expect them to be worth.&lt;/p&gt;
&lt;p&gt;The one I&amp;rsquo;m most confident about is the reaching. I know that reflex is there because I caught it earlier this year with autocomplete, opening a file and then waiting for the suggestion before I&amp;rsquo;d finished deciding what I wanted, so I don&amp;rsquo;t think the first thing I notice will be the slowness, I think it&amp;rsquo;ll be my hand going for the shortcut the way you reach for your phone in a lift. And it won&amp;rsquo;t happen on the hard parts either, it&amp;rsquo;ll happen on the small annoying ones, because the reflex has very little to do with difficulty and a great deal to do with not wanting to sit inside a pause.&lt;/p&gt;
&lt;p&gt;Then the slowness, which will be real and which I&amp;rsquo;m not going to dress up as something it isn&amp;rsquo;t. Some of it is just dull, because reading a changelog to find one flag isn&amp;rsquo;t craft, it&amp;rsquo;s admin, and doing it by hand doesn&amp;rsquo;t make it noble.&lt;/p&gt;
&lt;p&gt;And then the likeliest outcome of all, which is that I do two or three sessions and quit, not dramatically, but in exactly the way Orgsidian ended, by not being opened. If that&amp;rsquo;s what happens I&amp;rsquo;ll write that one down too, which is a much easier promise to make now, while I still think it&amp;rsquo;s unlikely.&lt;/p&gt;
&lt;h2 id="what-im-not-claiming"&gt;What I&amp;rsquo;m not claiming
&lt;/h2&gt;&lt;p&gt;This isn&amp;rsquo;t an argument, it&amp;rsquo;s one person and one project that hasn&amp;rsquo;t even started yet.&lt;/p&gt;
&lt;p&gt;I haven&amp;rsquo;t stopped using AI, I use it at work every day and I do it on purpose and I&amp;rsquo;d defend that, because refusing a tool that demonstrably works just because using it feels bad is superstition, and shipping is still the job.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not telling you to try this either. There&amp;rsquo;s a version of this post that ends in five bullet points about reclaiming your craft and I&amp;rsquo;d rather not write it, partly because I don&amp;rsquo;t know that any of this generalises and partly because I don&amp;rsquo;t know yet whether it&amp;rsquo;ll work on me.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m also fairly suspicious of the nostalgia running through all of this, because it&amp;rsquo;s very easy to mix up &amp;ldquo;coding was more fun before AI&amp;rdquo; with &amp;ldquo;coding was more fun when I was younger and had fewer responsibilities&amp;rdquo;, and some of what I&amp;rsquo;ve described here is probably the second one wearing the costume of the first, and since I can&amp;rsquo;t separate them cleanly I&amp;rsquo;m not going to pretend that I can.&lt;/p&gt;
&lt;p&gt;What I&amp;rsquo;ll say instead is narrower than all of that. What I lost wasn&amp;rsquo;t skill and it wasn&amp;rsquo;t time, it was the part of the work that happens while you&amp;rsquo;re doing it, and that turns out to be the part I was in it for.&lt;/p&gt;
&lt;h2 id="the-trap-i-can-already-see"&gt;The trap I can already see
&lt;/h2&gt;&lt;p&gt;There&amp;rsquo;s one failure mode worth naming now, while it&amp;rsquo;s still theoretical, and it&amp;rsquo;s that the pleasure quietly relocates, so that six months from now the good feeling starts arriving from writing about the sessions rather than from the sessions themselves, which would be the same substitution I&amp;rsquo;ve just spent an entire post describing, one level up, except that this time the artifact is the post.&lt;/p&gt;
&lt;p&gt;From the outside that would look identical to this going well, with output and consistency and a growing body of work to point at, and you&amp;rsquo;d only ever catch it from the inside, and probably late.&lt;/p&gt;
&lt;p&gt;So, for the record, the sessions are the project, and this is a note about them written before the first one.&lt;/p&gt;</description></item><item><title>Crossposting without the copy-paste: a GitHub Action from Hugo to dev.to</title><link>https://bstz.it/p/crossposting-without-the-copy-paste-a-github-action-from-hugo-to-dev.to/</link><pubDate>Sat, 18 Apr 2026 00:00:00 +0000</pubDate><guid>https://bstz.it/p/crossposting-without-the-copy-paste-a-github-action-from-hugo-to-dev.to/</guid><description>&lt;p&gt;My blog runs on Hugo. I also recently discovered I can cross-post to &lt;a class="link" href="https://dev.to" target="_blank" rel="noopener"
&gt;dev.to&lt;/a&gt;, because that&amp;rsquo;s where a lot of the conversation actually happens. For a while, my workflow was the obvious one: write the post, publish it here, open dev.to, paste it in, fix the frontmatter, fix the links, hit publish.&lt;/p&gt;
&lt;p&gt;It worked. I also stopped doing it after the second post.&lt;/p&gt;
&lt;p&gt;Copy-paste friction is the kind of chore that silently kills a publishing habit. So I did what most developers do when a task becomes repetitive: I automated it. And then I kept refining the automation until it was worth giving away.&lt;/p&gt;
&lt;p&gt;The result is &lt;a class="link" href="https://github.com/basteez/hugo-to-devto-action" target="_blank" rel="noopener"
&gt;&lt;code&gt;basteez/hugo-to-devto-action&lt;/code&gt;&lt;/a&gt;, now &lt;a class="link" href="https://github.com/marketplace/actions/hugo-to-dev-to-crosspost" target="_blank" rel="noopener"
&gt;published on the GitHub Marketplace&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="what-it-does"&gt;What it does
&lt;/h2&gt;&lt;p&gt;It&amp;rsquo;s a composite GitHub Action. On every push to your blog repo, it looks at what changed in the push range, finds new Hugo posts under your &lt;code&gt;content/post&lt;/code&gt; directory, and creates a corresponding &lt;strong&gt;draft&lt;/strong&gt; on dev.to for each one that is marked &lt;code&gt;draft: false&lt;/code&gt; locally.&lt;/p&gt;
&lt;p&gt;Key behaviours, chosen deliberately:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Drafts, not published posts.&lt;/strong&gt; The action never hits publish on dev.to for you. It mirrors content, but you stay in control of when it goes live on the other platform. This is intentional: dev.to has its own audience, its own timing, and its own editorial choices (cover image, tags, canonical URL). I want a starting point there&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dedup by title.&lt;/strong&gt; Before creating a draft, the action queries your dev.to account and skips anything whose title already exists. That means you can safely re-run on any push without ending up with duplicates, and editing a post on dev.to directly doesn&amp;rsquo;t get overwritten.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Push-range aware.&lt;/strong&gt; It uses &lt;code&gt;git diff&lt;/code&gt; across &lt;code&gt;github.event.before..github.event.after&lt;/code&gt; to find what actually changed, rather than scanning the whole repo every time. New posts get mirrored; edits to old posts don&amp;rsquo;t trigger spurious drafts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stateless.&lt;/strong&gt; There is no database, no cache, no state file in your repo. dev.to is the source of truth for &amp;ldquo;does this already exist?&amp;rdquo; If you ever want to start fresh, you can.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="using-it"&gt;Using it
&lt;/h2&gt;&lt;p&gt;The minimum viable workflow is about fifteen lines:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Crosspost to dev.to&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nt"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;push&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;branches&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;main]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nt"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;read&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nt"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;crosspost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runs-on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ubuntu-latest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;actions/checkout@v4&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fetch-depth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;basteez/hugo-to-devto-action@v1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;devto-api-key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ secrets.DEVTO_API_KEY }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;before&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ github.event.before }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;after&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ github.event.after }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Two things to watch for:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;fetch-depth: 0&lt;/code&gt; is not optional.&lt;/strong&gt; The default shallow checkout doesn&amp;rsquo;t contain the &lt;code&gt;before&lt;/code&gt; SHA of a real push, so &lt;code&gt;git diff&lt;/code&gt; has nothing to compare against. If you leave this out, the action will fail quickly and loudly. That failure mode is well understood; don&amp;rsquo;t go hunting for a mysterious bug.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;DEVTO_API_KEY&lt;/code&gt; lives in repo secrets.&lt;/strong&gt; Grab a Personal API Key from dev.to (Settings → Extensions → DEV Community API Keys) and add it as &lt;code&gt;DEVTO_API_KEY&lt;/code&gt; under your repo&amp;rsquo;s secrets. The action forwards it into the child process; it&amp;rsquo;s never logged.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If your posts live somewhere other than &lt;code&gt;content/post&lt;/code&gt;, pass &lt;code&gt;post-dir:&lt;/code&gt; with the path. If you want to see what would happen without actually creating drafts, pass &lt;code&gt;dry-run: 'true'&lt;/code&gt;. That&amp;rsquo;s the whole surface area.&lt;/p&gt;
&lt;h2 id="a-word-on-versioning"&gt;A word on versioning
&lt;/h2&gt;&lt;p&gt;I went with the GitHub convention of three pin styles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;@v1&lt;/code&gt; — a moving tag that tracks the latest non-breaking release. Bug fixes and additive features land automatically. This is what most people want.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@v1.x.y&lt;/code&gt; — a full semver pin if you need an immutable reference.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@&amp;lt;commit-sha&amp;gt;&lt;/code&gt; — a 40-character SHA for organisations that mandate commit-level pinning.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;v1&lt;/code&gt; tag will &lt;strong&gt;never&lt;/strong&gt; be force-updated to a breaking release. Breaking changes bump to &lt;code&gt;@v2&lt;/code&gt;. That way, the convenient default is also the safe default.&lt;/p&gt;
&lt;h2 id="why-bother-extracting-it"&gt;Why bother extracting it
&lt;/h2&gt;&lt;p&gt;This started life as a script in this blog&amp;rsquo;s own repo. It worked fine there. Extracting it into a standalone action was more work than copy-pasting a script into other Hugo repos would have been.&lt;/p&gt;
&lt;p&gt;I did it anyway, for three reasons.&lt;/p&gt;
&lt;p&gt;First, &lt;strong&gt;separation of concerns.&lt;/strong&gt; The crosspost logic has nothing to do with my blog content. Keeping them in the same repo was mixing &amp;ldquo;what I publish&amp;rdquo; with &amp;ldquo;how I publish it,&amp;rdquo; and every time I touched one, I had to reason about the other.&lt;/p&gt;
&lt;p&gt;Second, &lt;strong&gt;reuse.&lt;/strong&gt; I&amp;rsquo;m not the only person running a Hugo blog who&amp;rsquo;d like to mirror to dev.to. If the tool exists in a shareable form, other people can use it without forking my blog.&lt;/p&gt;
&lt;p&gt;Third, and this is the part I didn&amp;rsquo;t expect, &lt;strong&gt;extraction forces clarity.&lt;/strong&gt; When code is hiding inside your own repo, you get to take a lot of context for granted. The directory layout, the branching model, the workflow triggers, the assumptions about what&amp;rsquo;s already installed. Turning it into a public action meant writing all of that down as explicit inputs, documented defaults, and failure modes. The code ended up smaller and sharper.&lt;/p&gt;
&lt;p&gt;That last point keeps showing up in my work, and I think it&amp;rsquo;s worth naming: the act of making something reusable is often more valuable for the original project than for anyone who&amp;rsquo;ll reuse it.&lt;/p&gt;
&lt;h2 id="whats-next"&gt;What&amp;rsquo;s next
&lt;/h2&gt;&lt;p&gt;A few directions I&amp;rsquo;m considering, in rough priority order:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Optional publish mode, for people who genuinely do want one-click mirroring.&lt;/li&gt;
&lt;li&gt;Better handling of images and relative links, which is currently the biggest rough edge.&lt;/li&gt;
&lt;li&gt;Support for other destinations (Hashnode, Medium), though I&amp;rsquo;d rather keep this action focused and spin up siblings than turn it into a universal crossposter.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If any of this is useful to you, grab it from the &lt;a class="link" href="https://github.com/marketplace/actions/hugo-to-dev-to-crosspost" target="_blank" rel="noopener"
&gt;Marketplace&lt;/a&gt; or reference it directly as &lt;code&gt;basteez/hugo-to-devto-action@v1&lt;/code&gt;. Bug reports and PRs are welcome on &lt;a class="link" href="https://github.com/basteez/hugo-to-devto-action" target="_blank" rel="noopener"
&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And if this post shows up on my dev.to profile before I get around to publishing it there, you&amp;rsquo;ll know the action works.&lt;/p&gt;</description></item><item><title>BMAD in action: building TODOdoro</title><link>https://bstz.it/p/bmad-in-action-building-tododoro/</link><pubDate>Thu, 26 Mar 2026 10:00:00 +0000</pubDate><guid>https://bstz.it/p/bmad-in-action-building-tododoro/</guid><description>&lt;p&gt;&lt;strong&gt;This post is part of a series:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a class="link" href="https://bstz.it/p/dont-be-mad-bmad-instead/" &gt;Don&amp;rsquo;t be mad, BMAD instead&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://bstz.it/p/bmad-meet-the-crew/" &gt;BMAD: meet the crew&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;BMAD in action: building TODOdoro (you are here)&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;p&gt;This is the final post in my series about the BMAD Method. In &lt;a class="link" href="https://bstz.it/p/dont-be-mad-bmad-instead/" &gt;Part 1&lt;/a&gt;, I explained why I was skeptical of AI tools and how BMAD changed my mind. In &lt;a class="link" href="https://bstz.it/p/bmad-meet-the-crew/" &gt;Part 2&lt;/a&gt;, I introduced the agents and how they work together.&lt;/p&gt;
&lt;p&gt;Now I want to show you what BMAD looks like in practice.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/basteez/TODOdoro" target="_blank" rel="noopener"
&gt;TODOdoro&lt;/a&gt; is a productivity app I built using BMAD. It combines todo management with the Pomodoro technique, but (as you&amp;rsquo;ll see) it became something more interesting than that description suggests. The project is open source, still a work in progress, and all the BMAD artifacts are available in the repository if you want to explore them yourself.&lt;/p&gt;
&lt;p&gt;Let me walk you through the journey from initial idea to working code.&lt;/p&gt;
&lt;h2 id="the-starting-point"&gt;The starting point
&lt;/h2&gt;&lt;p&gt;I wanted to build a Pomodoro timer integrated with a todo app. Simple enough, right? Track tasks, run timers, see how many focused sessions each task required.&lt;/p&gt;
&lt;p&gt;That was the prompt I brought to the brainstorming session. It wasn&amp;rsquo;t wrong, but it wasn&amp;rsquo;t interesting either.&lt;/p&gt;
&lt;h2 id="brainstorming-where-the-magic-happened"&gt;Brainstorming: where the magic happened
&lt;/h2&gt;&lt;p&gt;I started with Mary (the Business Analyst) running a brainstorming session (&lt;code&gt;/bmad-agent-analyst&lt;/code&gt;, then selecting the brainstorming capability, or directly with &lt;code&gt;/bmad-brainstorming&lt;/code&gt;). The conversation began with practical questions: What tech stack? What features? How should the UX work?&lt;/p&gt;
&lt;p&gt;But Mary kept pushing deeper. She asked about the philosophy behind the tool. Why does tracking Pomodoros matter? What problem am I actually solving?&lt;/p&gt;
&lt;p&gt;The breakthrough came when the conversation shifted from features to first principles:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;A todo is not a reminder. It is a declaration of personal ownership. A Pomodoro is not a timer. It is a container for presence.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That reframing changed everything. Suddenly I wasn&amp;rsquo;t building a todo app with timers. I was building a mirror that reflects where your attention actually went.&lt;/p&gt;
&lt;p&gt;This is what I meant in Part 1 about agents asking the questions you should be asking yourself. I walked into the session with a feature list. I walked out with a philosophy.&lt;/p&gt;
&lt;h3 id="key-insights-from-brainstorming"&gt;Key insights from brainstorming
&lt;/h3&gt;&lt;p&gt;Several ideas emerged that shaped the entire product:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Session-first model.&lt;/strong&gt; Traditional apps put tasks first and time second. TODOdoro inverts this: the Pomodoro session is the primary entity. Todos are optional labels that sessions can belong to. You focus first, then reflect on what it meant.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Two kinds of letting go.&lt;/strong&gt; Most apps have a single &amp;ldquo;complete&amp;rdquo; action. But there&amp;rsquo;s a difference between &amp;ldquo;this served its purpose&amp;rdquo; and &amp;ldquo;this was never truly mine.&amp;rdquo; TODOdoro&amp;rsquo;s Release Ritual makes you choose, because the distinction matters.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Spatial priority.&lt;/strong&gt; Instead of priority fields (P1, P2, P3) or due dates, position on a canvas represents importance. Drag something to the center when it matters. No metadata to maintain, just visual intention.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Devotion Record.&lt;/strong&gt; Not a Pomodoro counter, but a timeline showing when and how much presence you invested. &amp;ldquo;11 Pomodoros across 9 days&amp;rdquo; tells a different story than &amp;ldquo;11 Pomodoros.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;The session generated 18 feature ideas, 9 architectural decisions, and 25+ edge cases to consider. All from a conversation that started with &amp;ldquo;I want a Pomodoro app.&amp;rdquo;&lt;/p&gt;
&lt;h2 id="from-philosophy-to-product-brief"&gt;From philosophy to product brief
&lt;/h2&gt;&lt;p&gt;After brainstorming, I worked with John (Product Manager) to create a product brief (&lt;code&gt;/bmad-product-brief&lt;/code&gt;). This document captured:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Target users.&lt;/strong&gt; Two personas emerged:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Marco, the &amp;ldquo;tool-fatigued maker&amp;rdquo; who&amp;rsquo;s exhausted by productivity overhead and wants honest visibility without guilt&lt;/li&gt;
&lt;li&gt;Sofia, the &amp;ldquo;invisible worker&amp;rdquo; whose research and writing produces slow, non-linear progress that traditional apps can&amp;rsquo;t acknowledge&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Core vision.&lt;/strong&gt; Your time and attention are acts of devotion, not units of output. The app is a personal mirror, not a task manager.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Design philosophy.&lt;/strong&gt; Radical respect for user autonomy. No streaks, no scores, no notifications. Settings contain only timer durations and theme options. A hard cap of 100 canvas cards prevents infinite accumulation.&lt;/p&gt;
&lt;p&gt;This brief became the foundation everything else built on. When questions arose later (should we add due dates? what about subtasks?), the brief provided clear answers: no, because they violate the philosophy.&lt;/p&gt;
&lt;h2 id="the-prd-philosophy-becomes-spec"&gt;The PRD: philosophy becomes spec
&lt;/h2&gt;&lt;p&gt;Working with John again (&lt;code&gt;/bmad-create-prd&lt;/code&gt;), I translated the product brief into a formal Product Requirements Document. This is where things got concrete.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Problem statement.&lt;/strong&gt; &amp;ldquo;You don&amp;rsquo;t plan to focus. You focus, then reflect on what it meant.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Success metrics.&lt;/strong&gt; Not vanity numbers, but signals of genuine value:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The user returns on Day 8 without notifications (the canvas feels personally owned)&lt;/li&gt;
&lt;li&gt;Canvas contains 10 or fewer curated cards&lt;/li&gt;
&lt;li&gt;Devotion Records span weeks on single intentions&lt;/li&gt;
&lt;li&gt;User describes the app with a personal &amp;ldquo;why,&amp;rdquo; not a feature summary&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Anti-metrics.&lt;/strong&gt; Signs the philosophy isn&amp;rsquo;t landing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;ldquo;Missing a due date field&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Need subtasks&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Session spam under 60 seconds&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;User journeys.&lt;/strong&gt; Concrete scenarios showing how Marco and Sofia would experience the product. Not wireframes, but narratives that capture intent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Functional requirements.&lt;/strong&gt; 31 specific requirements, each traceable to the philosophy. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;FR9: Release Ritual asks explicit question distinguishing purpose-completion from ownership rejection&lt;/li&gt;
&lt;li&gt;FR21-23: Devotion Record displays all sessions as a timeline across dates&lt;/li&gt;
&lt;li&gt;FR27-31: All state stored locally, offline-first, with schema versioning&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The PRD also established philosophical guardrails as non-negotiable requirements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No accounts, login, or server-side user data&lt;/li&gt;
&lt;li&gt;No due dates, priority fields, or subtasks&lt;/li&gt;
&lt;li&gt;No streaks, scores, or gamification&lt;/li&gt;
&lt;li&gt;Canvas hard cap of 100 cards (never configurable)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is what spec-driven development looks like. Every decision is written down, justified, and traceable.&lt;/p&gt;
&lt;h2 id="architecture-winston-weighs-in"&gt;Architecture: Winston weighs in
&lt;/h2&gt;&lt;p&gt;With requirements defined, I brought Winston (the Architect) into the conversation (&lt;code&gt;/bmad-create-architecture&lt;/code&gt;). His job was to translate the PRD into technical decisions.&lt;/p&gt;
&lt;p&gt;Key architectural choices:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Event sourcing.&lt;/strong&gt; Instead of storing current state, store all events that led to that state. This makes the Devotion Record trivial to implement (just replay session events) and enables powerful recovery from corruption.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Local-first with SQLite.&lt;/strong&gt; No accounts, no cloud. Data lives in the browser using SQLite compiled to WebAssembly (via SQLocal). The user owns their data completely.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Monorepo with strict boundaries.&lt;/strong&gt; A &lt;code&gt;@tododoro/domain&lt;/code&gt; package contains all business logic with zero external dependencies and 100% test coverage as a CI requirement. The domain is the philosophy encoded in TypeScript.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Repair pipeline.&lt;/strong&gt; If the app crashes mid-session, the repair pipeline detects orphaned events and fixes them automatically. The app always opens coherently regardless of what went wrong.&lt;/p&gt;
&lt;p&gt;Winston&amp;rsquo;s pragmatism showed here. He pushed back on complexity where it wasn&amp;rsquo;t needed, advocated for boring technology choices, and kept asking &amp;ldquo;what actually ships?&amp;rdquo;&lt;/p&gt;
&lt;h2 id="breaking-it-down-epics-and-stories"&gt;Breaking it down: epics and stories
&lt;/h2&gt;&lt;p&gt;Bob (Scrum Master) took the architecture and PRD and broke them into implementable chunks (&lt;code&gt;/bmad-create-epics-and-stories&lt;/code&gt;). Seven epics emerged:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Project Foundation &amp;amp; Domain Core&lt;/strong&gt; - Monorepo structure, event types, aggregates, 100% test coverage gate&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Constellation Canvas&lt;/strong&gt; - Spatial todo interface, drag positioning, pan/zoom, keyboard navigation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Pomodoro Session Loop&lt;/strong&gt; - Timer, session durability, resume/auto-complete on crash, exploration sessions&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Devotion Record&lt;/strong&gt; - Session history visualization, persistence across renames&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Todo Lifecycle &amp;amp; The Shelf&lt;/strong&gt; - Sealing, release ritual, eulogy for high-investment items, archive&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Production Storage &amp;amp; Data Durability&lt;/strong&gt; - SQLite migration, atomic writes, corruption tolerance, snapshots&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Settings &amp;amp; Personalisation&lt;/strong&gt; - Timer durations, themes, reduced-motion support&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The sequencing mattered. Epic 1 had to complete with 100% domain coverage before any UI work began. This enforced the philosophy: get the core logic right first, then build the interface on a solid foundation.&lt;/p&gt;
&lt;p&gt;Each epic broke down into stories with clear acceptance criteria. Before implementation, Bob prepared the sprint plan (&lt;code&gt;/bmad-sprint-planning&lt;/code&gt;) and created detailed story files (&lt;code&gt;/bmad-create-story&lt;/code&gt;). By the time Amelia (Developer) started coding, she knew exactly what to build.&lt;/p&gt;
&lt;h2 id="implementation-where-specs-pay-off"&gt;Implementation: where specs pay off
&lt;/h2&gt;&lt;p&gt;This is where the earlier work paid dividends. Let me explain how the actual story workflow operates.&lt;/p&gt;
&lt;h3 id="preparing-a-story"&gt;Preparing a story
&lt;/h3&gt;&lt;p&gt;Before implementation begins, you run &lt;code&gt;/bmad-create-story&lt;/code&gt;. This doesn&amp;rsquo;t just copy text from the epics file. It creates a comprehensive story document by:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Loading all your artifacts (PRD, architecture, UX specs, previous stories)&lt;/li&gt;
&lt;li&gt;Extracting everything relevant to this specific story&lt;/li&gt;
&lt;li&gt;Analyzing previous story files for patterns and learnings&lt;/li&gt;
&lt;li&gt;Checking git history for recent changes that might affect implementation&lt;/li&gt;
&lt;li&gt;Researching latest library versions if needed&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The output is a story file that contains:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;User story statement&lt;/strong&gt; (As a&amp;hellip; I want&amp;hellip; so that&amp;hellip;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Acceptance criteria&lt;/strong&gt; in BDD format (Given/When/Then)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tasks and subtasks&lt;/strong&gt; in exact implementation order&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dev Notes&lt;/strong&gt; with architecture requirements, file locations, testing patterns&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Previous story intelligence&lt;/strong&gt; (what worked, what didn&amp;rsquo;t, code patterns established)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The goal is to give the developer agent everything it needs without having to search through other documents.&lt;/p&gt;
&lt;h3 id="refining-stories-with-party-mode"&gt;Refining stories with Party Mode
&lt;/h3&gt;&lt;p&gt;Before handing a story to the developer, I run &lt;code&gt;/bmad-party-mode&lt;/code&gt; to refine it. I typically bring in Winston (Architect) and Quinn (QA) to review the story file together.&lt;/p&gt;
&lt;p&gt;They catch things the story creation missed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Winston might notice an architectural constraint that needs explicit mention&lt;/li&gt;
&lt;li&gt;Quinn asks about edge cases that should be acceptance criteria&lt;/li&gt;
&lt;li&gt;Both push back if tasks are too vague or too large&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This refinement step adds maybe 10 minutes per story, but it prevents hours of rework during implementation. The story that reaches Amelia has already survived cross-functional scrutiny.&lt;/p&gt;
&lt;h3 id="working-the-story"&gt;Working the story
&lt;/h3&gt;&lt;p&gt;When you run &lt;code&gt;/bmad-dev-story&lt;/code&gt;, Amelia (the Developer agent) follows a strict process:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Find and load the story.&lt;/strong&gt; She reads the complete story file and identifies the first incomplete task.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Mark in-progress.&lt;/strong&gt; The sprint status updates so you know what&amp;rsquo;s being worked on.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Execute tasks in exact order.&lt;/strong&gt; This is critical: the tasks are sequenced deliberately, and she follows them as written. No skipping ahead, no reordering.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Red-green-refactor for each task:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Write failing tests first (red)&lt;/li&gt;
&lt;li&gt;Implement minimal code to make tests pass (green)&lt;/li&gt;
&lt;li&gt;Refactor while keeping tests green&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Mark complete only when done.&lt;/strong&gt; A task checkbox only gets checked when tests actually exist and pass. No lying, no &amp;ldquo;I&amp;rsquo;ll add tests later.&amp;rdquo;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Update the story file.&lt;/strong&gt; Each completed task gets logged with what was implemented, what files changed, and any decisions made.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Continue until all tasks are done&lt;/strong&gt; or a blocking issue requires human input.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here&amp;rsquo;s what a real task looked like:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Task: Session completion and devotion dots&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Acceptance criteria:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When timer reaches zero, emit SessionCompleted event with actual duration&lt;/li&gt;
&lt;li&gt;Update Devotion Record projection with new session&lt;/li&gt;
&lt;li&gt;Display 6px amber dot on the todo card for each recorded Pomodoro&lt;/li&gt;
&lt;li&gt;Session under 60 seconds: abandon silently, no dot&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Amelia read this, wrote tests for each criterion, implemented until tests passed, then marked the task complete. The story file recorded exactly what happened.&lt;/p&gt;
&lt;h3 id="why-this-matters"&gt;Why this matters
&lt;/h3&gt;&lt;p&gt;The strict process prevents the chaos I&amp;rsquo;ve seen in other AI coding workflows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;No guessing.&lt;/strong&gt; The story file tells her exactly what to build.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No skipping tests.&lt;/strong&gt; Red-green-refactor is enforced, not suggested.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No mystery implementations.&lt;/strong&gt; The story file documents what was done.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No &amp;ldquo;works on my machine.&amp;rdquo;&lt;/strong&gt; Tests must pass before tasks complete.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When Amelia finished a story, I could review the story file and see exactly what happened. The gap between &amp;ldquo;what I asked for&amp;rdquo; and &amp;ldquo;what I got&amp;rdquo; was minimal because the spec had done its job.&lt;/p&gt;
&lt;h3 id="code-review-after-every-story"&gt;Code review after every story
&lt;/h3&gt;&lt;p&gt;After each story implementation, I run &lt;code&gt;/bmad-code-review&lt;/code&gt;. This isn&amp;rsquo;t optional for me, it&amp;rsquo;s part of the workflow.&lt;/p&gt;
&lt;p&gt;The code review runs multiple analysis passes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Looking for bugs and logic errors&lt;/li&gt;
&lt;li&gt;Checking edge cases that might have been missed&lt;/li&gt;
&lt;li&gt;Verifying the implementation matches the acceptance criteria&lt;/li&gt;
&lt;li&gt;Flagging code quality issues&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If the review finds problems, they get added as follow-up tasks in the story file. Amelia addresses them before the story is marked complete.&lt;/p&gt;
&lt;p&gt;One tip from the BMAD documentation: run the code review with a different LLM than the one that implemented the story. Fresh eyes catch more issues.&lt;/p&gt;
&lt;p&gt;The implementation phase moved fast because the thinking had already been done.&lt;/p&gt;
&lt;h2 id="what-surprised-me"&gt;What surprised me
&lt;/h2&gt;&lt;p&gt;A few things I didn&amp;rsquo;t expect:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The brainstorming session was the most valuable part.&lt;/strong&gt; I thought the dev work would be the main event. In hindsight, the conversation that turned &amp;ldquo;Pomodoro app&amp;rdquo; into &amp;ldquo;devotion mirror&amp;rdquo; was where the real value was created. Everything else was execution.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Specs prevented scope creep.&lt;/strong&gt; When I had ideas mid-implementation (&amp;ldquo;what if we added&amp;hellip;&amp;rdquo;), the specs gave me a clear answer: does this serve the philosophy? Usually the answer was no, and I moved on instead of getting distracted.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The agents caught blind spots.&lt;/strong&gt; In Party Mode (&lt;code&gt;/bmad-party-mode&lt;/code&gt;), I had Winston and Quinn review the architecture together. Quinn immediately asked about timer accuracy across browser backgrounding (a real problem I hadn&amp;rsquo;t considered). That became a specific requirement before any code was written.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Writing specs is thinking.&lt;/strong&gt; I used to view documentation as overhead. Now I see it as the actual work. The code is just the last step.&lt;/p&gt;
&lt;h2 id="what-didnt-work-perfectly"&gt;What didn&amp;rsquo;t work perfectly
&lt;/h2&gt;&lt;p&gt;BMAD isn&amp;rsquo;t magic. A few rough edges:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The upfront investment feels slow.&lt;/strong&gt; When you&amp;rsquo;re eager to code, spending hours on specs feels like procrastination. You have to trust the process. The payoff comes later, but it does come.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Some agent suggestions needed filtering.&lt;/strong&gt; The agents occasionally proposed features that conflicted with the philosophy I&amp;rsquo;d established. I had to stay vigilant and push back. The human is still the decision-maker.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The artifacts need maintenance.&lt;/strong&gt; When requirements changed mid-project, I had to update multiple documents. The chain of traceability is powerful but requires discipline.&lt;/p&gt;
&lt;h2 id="the-result"&gt;The result
&lt;/h2&gt;&lt;p&gt;TODOdoro exists. It&amp;rsquo;s &lt;a class="link" href="https://github.com/basteez/TODOdoro" target="_blank" rel="noopener"
&gt;open source on GitHub&lt;/a&gt;, still a work in progress, and includes all the BMAD artifacts in the &lt;code&gt;_bmad-output&lt;/code&gt; folder if you want to see exactly what I&amp;rsquo;ve described.&lt;/p&gt;
&lt;p&gt;More importantly, it&amp;rsquo;s something I actually use. The philosophy landed because I took the time to articulate it before writing code.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s the BMAD promise: not faster code, but better thinking. The code follows.&lt;/p&gt;
&lt;h2 id="try-it-yourself"&gt;Try it yourself
&lt;/h2&gt;&lt;p&gt;If this series has made you curious, here&amp;rsquo;s how to start:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install BMAD: &lt;code&gt;npx bmad-method install&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Start with &lt;code&gt;/bmad-agent-pm&lt;/code&gt; and describe your idea&lt;/li&gt;
&lt;li&gt;Let John ask you uncomfortable questions&lt;/li&gt;
&lt;li&gt;See where it leads&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You don&amp;rsquo;t need a big project. Try it on something small. A software config. A CLI tool. Something you want to reproduce.&lt;/p&gt;
&lt;p&gt;The process will feel strange at first. You&amp;rsquo;ll want to skip ahead. Don&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;The thinking is the work. The code is just the last step.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Resources:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://docs.bmad-method.org/" target="_blank" rel="noopener"
&gt;BMAD Method documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/bmad-code-org/BMAD-METHOD" target="_blank" rel="noopener"
&gt;BMAD on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/basteez/TODOdoro" target="_blank" rel="noopener"
&gt;TODOdoro on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://bstz.it/p/dont-be-mad-bmad-instead/" &gt;Part 1: Don&amp;rsquo;t be mad, BMAD instead&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://bstz.it/p/bmad-meet-the-crew/" &gt;Part 2: Meet the crew&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>BMAD: meet the crew</title><link>https://bstz.it/p/bmad-meet-the-crew/</link><pubDate>Thu, 26 Mar 2026 09:00:00 +0000</pubDate><guid>https://bstz.it/p/bmad-meet-the-crew/</guid><description>&lt;p&gt;&lt;strong&gt;This post is part of a series:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a class="link" href="https://bstz.it/p/dont-be-mad-bmad-instead/" &gt;Don&amp;rsquo;t be mad, BMAD instead&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;BMAD: meet the crew (you are here)&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://bstz.it/p/bmad-in-action-building-tododoro/" &gt;BMAD in action: building TODOdoro&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;p&gt;In the &lt;a class="link" href="https://bstz.it/p/dont-be-mad-bmad-instead/" &gt;previous post&lt;/a&gt;, I talked about why I was skeptical of AI coding tools and how BMAD changed my perspective. The short version: it forces you to think before you code, and it keeps you in the driver&amp;rsquo;s seat.&lt;/p&gt;
&lt;p&gt;But I glossed over the most distinctive part of the framework: the agents.&lt;/p&gt;
&lt;p&gt;BMAD isn&amp;rsquo;t one AI assistant. It&amp;rsquo;s a team of specialized personas, each with a distinct role, communication style, and area of expertise. When you work with BMAD, you&amp;rsquo;re not just prompting a generic model. You&amp;rsquo;re having conversations with characters who push back, ask questions, and hold you accountable to their domain.&lt;/p&gt;
&lt;p&gt;It sounds gimmicky. It&amp;rsquo;s not.&lt;/p&gt;
&lt;h2 id="why-personas-matter"&gt;Why personas matter
&lt;/h2&gt;&lt;p&gt;The obvious answer is specialization. A product manager thinks differently than an architect. A QA engineer asks different questions than a UX designer. By splitting these roles into separate agents, BMAD ensures that each phase of development gets the attention it deserves.&lt;/p&gt;
&lt;p&gt;But there&amp;rsquo;s a subtler benefit: context switching becomes intentional.&lt;/p&gt;
&lt;p&gt;When you invoke the Product Manager, you&amp;rsquo;re not just asking for PRD help. You&amp;rsquo;re stepping into a conversation about user needs, market positioning, and what problem you&amp;rsquo;re actually solving. When you switch to the Architect, the entire frame shifts to technical constraints, scalability, and system design.&lt;/p&gt;
&lt;p&gt;This forced perspective-taking is surprisingly valuable. It&amp;rsquo;s easy to skip straight to implementation when you&amp;rsquo;re working alone. The agents won&amp;rsquo;t let you. Each one guards their domain, and you have to satisfy them before moving forward.&lt;/p&gt;
&lt;h2 id="meet-the-crew"&gt;Meet the crew
&lt;/h2&gt;&lt;p&gt;BMAD&amp;rsquo;s core team consists of nine agents. Each has a name, a personality, and a specific set of capabilities. Here&amp;rsquo;s who you&amp;rsquo;ll be working with:&lt;/p&gt;
&lt;h3 id="john-product-manager"&gt;John (Product Manager)
&lt;/h3&gt;&lt;p&gt;John is a relentless questioner. His job is to figure out what you&amp;rsquo;re actually building and why. He&amp;rsquo;ll push you on user needs, challenge your assumptions, and refuse to let you start until you can articulate the problem clearly.&lt;/p&gt;
&lt;p&gt;His style is direct and data-sharp. He cuts through fluff to find what actually matters. If you come to him with a vague idea, expect to leave with a focused problem statement.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What he does:&lt;/strong&gt; PRD creation, requirements discovery, epic and story generation, implementation readiness checks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Command:&lt;/strong&gt; &lt;code&gt;/bmad-agent-pm&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;When to call him:&lt;/strong&gt; At the very beginning, when you have an idea but haven&amp;rsquo;t defined what &amp;ldquo;done&amp;rdquo; looks like.&lt;/p&gt;
&lt;h3 id="mary-business-analyst"&gt;Mary (Business Analyst)
&lt;/h3&gt;&lt;p&gt;Mary treats every business challenge like a treasure hunt. She&amp;rsquo;s the one who digs into market research, competitive analysis, and domain expertise. Where John focuses on what to build, Mary focuses on the landscape you&amp;rsquo;re building in.&lt;/p&gt;
&lt;p&gt;Her communication style is energetic and discovery-oriented. She gets excited when patterns emerge and uses frameworks (Porter&amp;rsquo;s Five Forces, SWOT) naturally without making it feel academic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What she does:&lt;/strong&gt; Market research, competitive analysis, domain deep-dives, requirements elicitation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Command:&lt;/strong&gt; &lt;code&gt;/bmad-agent-analyst&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;When to call her:&lt;/strong&gt; When you need to understand the space before committing to a direction.&lt;/p&gt;
&lt;h3 id="winston-architect"&gt;Winston (Architect)
&lt;/h3&gt;&lt;p&gt;Winston is the calm pragmatist. He balances &amp;ldquo;what could be&amp;rdquo; with &amp;ldquo;what should be,&amp;rdquo; grounding every recommendation in real-world trade-offs. His expertise covers distributed systems, cloud infrastructure, and API design.&lt;/p&gt;
&lt;p&gt;He speaks in measured tones, always connecting technical decisions to business value. He&amp;rsquo;ll advocate for boring technology over shiny new frameworks, because boring ships.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What he does:&lt;/strong&gt; Technical architecture decisions, system design, scalability planning.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Command:&lt;/strong&gt; &lt;code&gt;/bmad-agent-architect&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;When to call him:&lt;/strong&gt; After the requirements are clear, when you need to decide how the pieces fit together.&lt;/p&gt;
&lt;h3 id="sally-ux-designer"&gt;Sally (UX Designer)
&lt;/h3&gt;&lt;p&gt;Sally is an empathetic advocate who paints pictures with words. She tells user stories that make you feel the problem, not just understand it. Her focus is on genuine user needs, and she balances creativity with attention to edge cases.&lt;/p&gt;
&lt;p&gt;Her approach is simple: start simple, evolve through feedback. Every design decision should serve a real person.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What she does:&lt;/strong&gt; UX planning, interaction design, experience strategy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Command:&lt;/strong&gt; &lt;code&gt;/bmad-agent-ux-designer&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;When to call her:&lt;/strong&gt; When you need to think through how users will actually interact with what you&amp;rsquo;re building.&lt;/p&gt;
&lt;h3 id="bob-scrum-master"&gt;Bob (Scrum Master)
&lt;/h3&gt;&lt;p&gt;Bob is crisp and checklist-driven. Every word has a purpose, every requirement crystal clear. He has zero tolerance for ambiguity, which makes him invaluable for turning vague plans into actionable work.&lt;/p&gt;
&lt;p&gt;He&amp;rsquo;s also a servant leader who genuinely enjoys discussing agile process and theory. If you want to debate sprint structures or story formats, he&amp;rsquo;s your guy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What he does:&lt;/strong&gt; Sprint planning, story preparation, retrospectives, course correction when things go sideways.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Command:&lt;/strong&gt; &lt;code&gt;/bmad-agent-sm&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;When to call him:&lt;/strong&gt; When requirements are defined and you need to break them into implementable chunks.&lt;/p&gt;
&lt;h3 id="amelia-developer"&gt;Amelia (Developer)
&lt;/h3&gt;&lt;p&gt;Amelia is ultra-precise. She speaks in file paths and acceptance criteria IDs. No fluff, all precision. Her job is to execute approved stories with strict adherence to the specs you&amp;rsquo;ve created.&lt;/p&gt;
&lt;p&gt;She&amp;rsquo;s test-driven to the core. Every task gets tests before it&amp;rsquo;s marked complete, and she won&amp;rsquo;t proceed with failing tests. If you&amp;rsquo;ve done the specification work properly, Amelia turns it into working code without surprises.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What she does:&lt;/strong&gt; Story implementation, code review, test-driven development.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Command:&lt;/strong&gt; &lt;code&gt;/bmad-agent-dev&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;When to call her:&lt;/strong&gt; When the spec is ready and it&amp;rsquo;s time to write code.&lt;/p&gt;
&lt;h3 id="quinn-qa-engineer"&gt;Quinn (QA Engineer)
&lt;/h3&gt;&lt;p&gt;Quinn is pragmatic and straightforward. Her philosophy is &amp;ldquo;ship it and iterate,&amp;rdquo; focusing on getting test coverage fast without overthinking. She generates tests for existing features using standard framework patterns.&lt;/p&gt;
&lt;p&gt;She&amp;rsquo;s the counterbalance to perfectionism. Coverage first, optimization later.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What she does:&lt;/strong&gt; API and E2E test generation, test automation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Command:&lt;/strong&gt; &lt;code&gt;/bmad-agent-qa&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;When to call her:&lt;/strong&gt; After implementation, when you need to verify that what you built actually works.&lt;/p&gt;
&lt;h3 id="paige-tech-writer"&gt;Paige (Tech Writer)
&lt;/h3&gt;&lt;p&gt;Paige is a patient educator who explains complex concepts like teaching a friend. She transforms technical details into accessible, structured documentation, using analogies that make the complex simple.&lt;/p&gt;
&lt;p&gt;She&amp;rsquo;s a master of clarity. Every word serves a purpose, and diagrams often speak louder than paragraphs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What she does:&lt;/strong&gt; Documentation, technical explanations, Mermaid diagrams, document validation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Command:&lt;/strong&gt; &lt;code&gt;/bmad-agent-tech-writer&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;When to call her:&lt;/strong&gt; When you need to explain what you&amp;rsquo;ve built (to users, to teammates, or to your future self).&lt;/p&gt;
&lt;h3 id="barry-quick-flow-solo-dev"&gt;Barry (Quick Flow Solo Dev)
&lt;/h3&gt;&lt;p&gt;Barry is the speedrunner. He handles &amp;ldquo;Quick Flow&amp;rdquo; (smaller tasks that don&amp;rsquo;t need the full ceremony), moving from tech spec to implementation with ruthless efficiency.&lt;/p&gt;
&lt;p&gt;His style is direct and implementation-focused. Minimum bureaucracy, maximum results. If you have a well-defined small task, Barry gets it done.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What he does:&lt;/strong&gt; Rapid spec-to-implementation for smaller tasks, code review.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Command:&lt;/strong&gt; &lt;code&gt;/bmad-agent-quick-flow-solo-dev&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;When to call him:&lt;/strong&gt; For bug fixes, small features, or tasks where the full workflow would be overkill.&lt;/p&gt;
&lt;h2 id="the-workflow-how-agents-hand-off"&gt;The workflow: how agents hand off
&lt;/h2&gt;&lt;p&gt;The agents aren&amp;rsquo;t meant to be used in isolation. They form a pipeline:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;John&lt;/strong&gt; (PM) clarifies what you&amp;rsquo;re building and creates the PRD&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mary&lt;/strong&gt; (Analyst) provides market and domain context if needed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Winston&lt;/strong&gt; (Architect) designs the technical approach&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sally&lt;/strong&gt; (UX) plans the user experience&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bob&lt;/strong&gt; (SM) breaks everything into stories and plans sprints&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Amelia&lt;/strong&gt; (Dev) implements the stories&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Quinn&lt;/strong&gt; (QA) verifies the implementation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Paige&lt;/strong&gt; (Tech Writer) documents the result&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You don&amp;rsquo;t always need every step. A small bug fix might go straight to Barry. A well-understood feature might skip Mary&amp;rsquo;s research phase. But for anything substantial, the handoffs matter.&lt;/p&gt;
&lt;p&gt;Each agent produces artifacts that the next agent consumes. John&amp;rsquo;s PRD feeds Winston&amp;rsquo;s architecture. Winston&amp;rsquo;s architecture feeds Bob&amp;rsquo;s stories. Bob&amp;rsquo;s stories feed Amelia&amp;rsquo;s implementation. The chain ensures that nothing gets lost in translation.&lt;/p&gt;
&lt;h2 id="party-mode-when-agents-collaborate"&gt;Party Mode: when agents collaborate
&lt;/h2&gt;&lt;p&gt;Sometimes you need multiple perspectives at once. That&amp;rsquo;s where Party Mode comes in.&lt;/p&gt;
&lt;p&gt;Party Mode (&lt;code&gt;/bmad-party-mode&lt;/code&gt;) lets you have a conversation with several agents simultaneously. You might be discussing a technical decision and want both Winston (Architect) and Quinn (QA) to weigh in. Or you&amp;rsquo;re reviewing a PRD and want John, Sally, and Bob to debate the trade-offs.&lt;/p&gt;
&lt;p&gt;The agents stay in character, each bringing their own priorities and concerns. Winston might argue for a simpler architecture while Sally pushes for a richer user experience. Bob might point out that the current scope won&amp;rsquo;t fit in a sprint. You moderate, listen to the perspectives, and make the call.&lt;/p&gt;
&lt;p&gt;It sounds like a gimmick. In practice, it&amp;rsquo;s one of the most useful features for surfacing blind spots. Different roles genuinely do see different risks, and hearing them argue (politely) helps you make better decisions.&lt;/p&gt;
&lt;p&gt;Party Mode is particularly useful for refining artifacts you&amp;rsquo;ve already created. Got a PRD that feels incomplete? Throw it into a party with the PM, Architect, and UX Designer. They&amp;rsquo;ll poke holes from their respective angles, and you&amp;rsquo;ll walk away with a tighter spec. Same goes for architecture documents, story breakdowns, or anything else that benefits from cross-functional scrutiny. Refinement is where Party Mode really shines.&lt;/p&gt;
&lt;h2 id="choosing-your-path"&gt;Choosing your path
&lt;/h2&gt;&lt;p&gt;BMAD is flexible about how much ceremony you need:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Full workflow:&lt;/strong&gt; For new products or major features, start with John, work through the entire pipeline, and produce comprehensive specs before implementation. This is the safest path for anything complex.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Quick Flow:&lt;/strong&gt; For smaller tasks (bug fixes, minor features, well-understood changes), call Barry and move fast. He&amp;rsquo;ll still push you to clarify intent and think through edge cases, but without the full artifact chain.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mix and match:&lt;/strong&gt; Need architecture help but already have requirements? Call Winston directly. Want to validate a PRD you wrote yourself? John can review it. The agents are tools, not a rigid process.&lt;/p&gt;
&lt;p&gt;The key insight is that the structure exists to help you think, not to slow you down. Use as much as you need. Skip what doesn&amp;rsquo;t serve you.&lt;/p&gt;
&lt;h2 id="whats-next"&gt;What&amp;rsquo;s next
&lt;/h2&gt;&lt;p&gt;In the final post of this series, I&amp;rsquo;ll walk through a real project I built with BMAD: &lt;a class="link" href="https://github.com/basteez/TODOdoro" target="_blank" rel="noopener"
&gt;TODOdoro&lt;/a&gt;, a productivity app that combines todo management with the Pomodoro technique.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll show how the process actually looked in practice: what questions John asked, what architecture Winston proposed, how the stories got structured, and where the agents surprised me (for better and worse).&lt;/p&gt;
&lt;p&gt;If you want to see BMAD in action rather than just described, that&amp;rsquo;s the post for you.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Resources:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://docs.bmad-method.org/" target="_blank" rel="noopener"
&gt;BMAD Method documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/bmad-code-org/BMAD-METHOD" target="_blank" rel="noopener"
&gt;BMAD on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://bstz.it/p/dont-be-mad-bmad-instead/" &gt;Part 1: Don&amp;rsquo;t be mad, BMAD instead&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Don't be mad, BMAD instead</title><link>https://bstz.it/p/dont-be-mad-bmad-instead/</link><pubDate>Thu, 26 Mar 2026 08:00:00 +0000</pubDate><guid>https://bstz.it/p/dont-be-mad-bmad-instead/</guid><description>&lt;p&gt;&lt;strong&gt;This post is part of a series:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Don&amp;rsquo;t be mad, BMAD instead (you are here)&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://bstz.it/p/bmad-meet-the-crew/" &gt;BMAD: meet the crew&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://bstz.it/p/bmad-in-action-building-tododoro/" &gt;BMAD in action: building TODOdoro&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;p&gt;A few weeks ago I wrote about the tension I felt using AI coding tools, the fear that by outsourcing the friction, I was also outsourcing my competence. That piece ended with a question: are you extending yourself, or outsourcing yourself?&lt;/p&gt;
&lt;p&gt;I didn&amp;rsquo;t have a clean answer then. I still don&amp;rsquo;t have a universal one. But I found something that changed how I think about the problem.&lt;/p&gt;
&lt;h2 id="the-grave-i-thought-i-was-digging"&gt;The grave I thought I was digging
&lt;/h2&gt;&lt;p&gt;When a colleague first mentioned BMAD, my reaction was skepticism dressed as caution. Another AI framework. Another promise that the machine will handle it. My concern wasn&amp;rsquo;t that it wouldn&amp;rsquo;t work, it was that it would work too well, and I&amp;rsquo;d wake up one day having forgotten how to build things myself.&lt;/p&gt;
&lt;p&gt;That fear isn&amp;rsquo;t irrational. I&amp;rsquo;ve seen what happens when you reach for autocomplete before you&amp;rsquo;ve finished the thought. You produce, but you don&amp;rsquo;t learn. The output looks fine, but the map never forms. You become dependent on a system you can&amp;rsquo;t fully reason about, and every time you hit a similar problem, you prompt again instead of drawing from understanding.&lt;/p&gt;
&lt;p&gt;So when someone told me &amp;ldquo;try this method where AI agents play different roles,&amp;rdquo; I heard: more ways to stop thinking. More automation. More distance between me and the craft I&amp;rsquo;d spent years developing.&lt;/p&gt;
&lt;p&gt;I was wrong. But it took some hands-on experimentation to see why.&lt;/p&gt;
&lt;h2 id="the-problem-with-vibe-coding"&gt;The problem with vibe coding
&lt;/h2&gt;&lt;p&gt;Before I explain what BMAD does differently, let me describe what it&amp;rsquo;s pushing against.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a pattern that&amp;rsquo;s become disturbingly common, often celebrated even, that I&amp;rsquo;ll call &amp;ldquo;vibe coding.&amp;rdquo; It goes like this: you have a rough idea of what you want to build. You open your editor, write a prompt, and let the AI generate something. If it looks mostly right, you keep it. If it doesn&amp;rsquo;t work, you prompt again with a correction. You iterate like this, adjusting and regenerating, until the output seems functional.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve always hated this approach. It treats software development like a slot machine: pull the lever, see what comes out, try again if you don&amp;rsquo;t like it.&lt;/p&gt;
&lt;p&gt;But here&amp;rsquo;s what&amp;rsquo;s actually happening: you&amp;rsquo;re making decisions without articulating them. You&amp;rsquo;re accepting code without fully understanding it. You&amp;rsquo;re building something that works (maybe) without building a mental model of why it works. The AI is filling in gaps you never consciously identified, which means you can&amp;rsquo;t evaluate whether it filled them correctly.&lt;/p&gt;
&lt;p&gt;Vibe coding optimizes for output. It feels efficient in the moment. But it produces fragile results, because no one (not you, not the AI) ever defined what &amp;ldquo;correct&amp;rdquo; actually means. When something breaks later, you&amp;rsquo;re debugging a system you never fully understood in the first place.&lt;/p&gt;
&lt;p&gt;This isn&amp;rsquo;t a problem with AI. It&amp;rsquo;s a problem with how we use it. And it&amp;rsquo;s the problem that spec-driven development solves.&lt;/p&gt;
&lt;h2 id="what-spec-driven-development-actually-means"&gt;What spec-driven development actually means
&lt;/h2&gt;&lt;p&gt;If you haven&amp;rsquo;t encountered the term before, spec-driven development is exactly what it sounds like: you write the specification before you write the code.&lt;/p&gt;
&lt;p&gt;That might seem obvious. &amp;ldquo;Of course you should know what you&amp;rsquo;re building before you build it.&amp;rdquo; But there&amp;rsquo;s a difference between having a vague idea in your head and having a written document that defines requirements, constraints, acceptance criteria, and architectural decisions.&lt;/p&gt;
&lt;p&gt;The spec isn&amp;rsquo;t just documentation. It&amp;rsquo;s a thinking tool. The act of writing it forces you to confront questions you might otherwise skip: What exactly is this feature supposed to do? What happens at the edges? How does this component interact with that one? What does &amp;ldquo;done&amp;rdquo; look like?&lt;/p&gt;
&lt;p&gt;When you answer these questions upfront, implementation becomes a translation exercise. You&amp;rsquo;re not discovering the requirements as you code. You&amp;rsquo;re executing a plan you&amp;rsquo;ve already validated. The code follows from decisions you consciously made, not from guesses the AI filled in for you.&lt;/p&gt;
&lt;p&gt;This is the opposite of vibe coding. Instead of iterating toward something that seems to work, you define what &amp;ldquo;working&amp;rdquo; means first, then build to that definition.&lt;/p&gt;
&lt;p&gt;The challenge, of course, is that writing good specs is hard. It requires discipline, experience, and (honestly) a fair amount of tedium. Which is exactly where BMAD comes in.&lt;/p&gt;
&lt;h2 id="what-bmad-actually-is"&gt;What BMAD actually is
&lt;/h2&gt;&lt;p&gt;BMAD stands for &amp;ldquo;Build More Architect Dreams.&amp;rdquo; It&amp;rsquo;s an open-source framework that uses AI agents to guide you through a structured development process, from initial idea through implementation.&lt;/p&gt;
&lt;p&gt;But here&amp;rsquo;s the crucial part: BMAD doesn&amp;rsquo;t generate code and call it done. It forces you through the specification process first.&lt;/p&gt;
&lt;p&gt;The framework includes multiple specialized agents, each with a distinct role:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;Product Manager&lt;/strong&gt; who helps you clarify what you&amp;rsquo;re building and why&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;Business Analyst&lt;/strong&gt; who digs into requirements and edge cases&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;Architect&lt;/strong&gt; who designs how components will fit together&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;Developer&lt;/strong&gt; who implements based on the specs you&amp;rsquo;ve created&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;QA Engineer&lt;/strong&gt; who defines how you&amp;rsquo;ll verify the implementation works&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each agent asks questions. Each one pushes you to articulate something specific before you move forward. You can&amp;rsquo;t skip to the code, because the developer agent won&amp;rsquo;t have what it needs until the earlier stages are complete.&lt;/p&gt;
&lt;p&gt;This structure is the key insight. The AI isn&amp;rsquo;t doing the thinking for you. It&amp;rsquo;s holding you accountable to a process that requires thinking. The agents are collaborators, not replacements. They augment your capability to do the specification work, but the decisions remain yours.&lt;/p&gt;
&lt;h2 id="why-this-feels-different"&gt;Why this feels different
&lt;/h2&gt;&lt;p&gt;Most AI coding tools are optimized for a simple interaction: you prompt, you get code. The faster and more accurate that loop, the better the tool seems.&lt;/p&gt;
&lt;p&gt;BMAD optimizes for something else: the quality of your thinking before code exists.&lt;/p&gt;
&lt;p&gt;When I started experimenting with it, I chose deliberately small tasks. An Emacs configuration I&amp;rsquo;d been putting off. A bug I wanted to reproduce in isolation before fixing. Low stakes, easy to abandon if it felt like hype.&lt;/p&gt;
&lt;p&gt;What surprised me wasn&amp;rsquo;t the speed. It was the structure.&lt;/p&gt;
&lt;p&gt;Working with the Product Manager agent, I had to articulate what I actually wanted (not just &amp;ldquo;fix this bug,&amp;rdquo; but what specific behavior should change, and why, and how I&amp;rsquo;d know it was fixed). Working with the Architect, I had to think about how my change would interact with existing code. Working with the QA persona, I had to define acceptance criteria before implementation.&lt;/p&gt;
&lt;p&gt;By the time I reached the Developer agent, I wasn&amp;rsquo;t prompting and hoping. I was handing over a spec that I understood, because I&amp;rsquo;d helped build it. The implementation felt like execution, not experimentation.&lt;/p&gt;
&lt;p&gt;And here&amp;rsquo;s what I didn&amp;rsquo;t expect: that constraint is liberating. The process felt slower at first (more steps, more questions, more writing). But I wasn&amp;rsquo;t backtracking. I wasn&amp;rsquo;t debugging code I didn&amp;rsquo;t understand. I wasn&amp;rsquo;t prompting the same thing five different ways hoping for a better result.&lt;/p&gt;
&lt;p&gt;The spec had done its job. The thinking was front-loaded, where it belongs.&lt;/p&gt;
&lt;h2 id="orchestrator-not-passenger"&gt;Orchestrator, not passenger
&lt;/h2&gt;&lt;p&gt;The shift I described in my earlier post, from active thinker to passive prompter, doesn&amp;rsquo;t happen with BMAD. It can&amp;rsquo;t. The method requires you to validate, decide, and course-correct at every stage.&lt;/p&gt;
&lt;p&gt;The AI handles the grunt work: generating document templates, suggesting architectural patterns, writing implementation code that follows the spec. But you&amp;rsquo;re still the one reviewing, adjusting, and approving. You&amp;rsquo;re still the one who understands why the system is designed the way it is.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s the answer I was looking for. Not &amp;ldquo;don&amp;rsquo;t use AI,&amp;rdquo; but &amp;ldquo;use it in a way that keeps you in the driver&amp;rsquo;s seat.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;I delegate the dirty work. I orchestrate. I stay in the loop.&lt;/p&gt;
&lt;p&gt;The mental model I keep coming back to is this: BMAD turns AI into a team of junior collaborators who are fast and capable, but who need direction. You&amp;rsquo;re the senior engineer. You set the vision, define the constraints, and make the judgment calls. They execute, and they execute well, but only because you&amp;rsquo;ve told them what &amp;ldquo;well&amp;rdquo; means.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s extending yourself. That&amp;rsquo;s the version of AI assistance I was hoping existed.&lt;/p&gt;
&lt;h2 id="the-dopamine-is-real-and-so-is-the-fun"&gt;The dopamine is real (and so is the fun)
&lt;/h2&gt;&lt;p&gt;I should mention something else: this process is genuinely enjoyable.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a particular satisfaction in watching a well-defined spec turn into working code without surprises. The gap between &amp;ldquo;what I wanted&amp;rdquo; and &amp;ldquo;what I got&amp;rdquo; shrinks dramatically when you&amp;rsquo;ve done the specification work upfront.&lt;/p&gt;
&lt;p&gt;And (I didn&amp;rsquo;t expect this) role-playing with the different personas is fun. Switching from the PM mindset to the Architect mindset to the Developer mindset keeps the work varied. You&amp;rsquo;re not just grinding through one mode of thinking. You&amp;rsquo;re shifting perspectives, which keeps you engaged.&lt;/p&gt;
&lt;p&gt;BMAD even has something called &amp;ldquo;Party Mode&amp;rdquo; where multiple agents can participate in a single conversation, debating approaches and surfacing trade-offs. It sounds gimmicky until you try it. Having the Architect and QA agents push back on each other while you moderate is surprisingly useful for finding blind spots.&lt;/p&gt;
&lt;h2 id="whats-next"&gt;What&amp;rsquo;s next
&lt;/h2&gt;&lt;p&gt;This is the first in a series of three posts.&lt;/p&gt;
&lt;p&gt;In the next one, I&amp;rsquo;ll go deeper on the agents themselves: who they are, what each one does, how they hand off work to each other, and how you can customize the process for your own workflow.&lt;/p&gt;
&lt;p&gt;In the third post, I&amp;rsquo;ll walk through a real project I built using BMAD: a productivity app called &lt;a class="link" href="https://github.com/basteez/TODOdoro" target="_blank" rel="noopener"
&gt;TODOdoro&lt;/a&gt;. It combines todo management with the Pomodoro technique, and I&amp;rsquo;ll show how the method helped me go from a philosophical idea (&amp;ldquo;a mirror, not a manager&amp;rdquo;) to a working application with a clear architecture.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve felt the same tension I did, excited by AI&amp;rsquo;s potential but worried about what you might lose, this might be worth following. BMAD isn&amp;rsquo;t the only answer, but it&amp;rsquo;s an answer. And for me, it&amp;rsquo;s been the difference between feeling like AI is happening to me and feeling like I&amp;rsquo;m using it deliberately.&lt;/p&gt;
&lt;p&gt;The tools are getting more powerful every month. The question isn&amp;rsquo;t whether to use them. It&amp;rsquo;s whether you&amp;rsquo;ll use them in a way that makes you better, or in a way that quietly makes you less than you were.&lt;/p&gt;
&lt;p&gt;I know which one I&amp;rsquo;m choosing.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Resources:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://docs.bmad-method.org/" target="_blank" rel="noopener"
&gt;BMAD Method documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/bmad-code-org/BMAD-METHOD" target="_blank" rel="noopener"
&gt;BMAD on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>AI and the Craft: are we extending ourselves, or outsourcing ourselves?</title><link>https://bstz.it/p/ai-and-the-craft-are-we-extending-ourselves-or-outsourcing-ourselves/</link><pubDate>Tue, 03 Mar 2026 00:00:00 +0000</pubDate><guid>https://bstz.it/p/ai-and-the-craft-are-we-extending-ourselves-or-outsourcing-ourselves/</guid><description>&lt;p&gt;There&amp;rsquo;s a moment, when an idea you&amp;rsquo;ve been carrying around for months starts taking shape in a matter of hours, that feels genuinely new. Not just faster, different. I&amp;rsquo;ve had that feeling more than once over the past year working with AI coding tools, and I&amp;rsquo;d be dishonest if I said it wasn&amp;rsquo;t exciting.&lt;/p&gt;
&lt;p&gt;But somewhere along the way, I started asking a different question.&lt;/p&gt;
&lt;h2 id="how-much-of-this-is-mine"&gt;How much of this is mine?
&lt;/h2&gt;&lt;p&gt;When AI helps you build something (suggesting the structure, filling in the implementation, catching the edge cases), how much of that project actually belongs to you? Not in a legal sense. In a competence sense.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not being precious about writing code by hand. But here&amp;rsquo;s the thing about friction: it&amp;rsquo;s not just an obstacle. It&amp;rsquo;s actually the mechanism by which understanding forms.&lt;/p&gt;
&lt;p&gt;When you get stuck on a problem and have to dig yourself out, something happens. You look at the internals. You read the error message carefully. You build a mental model of why something behaves the way it does. That model stays with you. It becomes pattern recognition you can apply the next time. The path from &amp;ldquo;I wrote it&amp;rdquo; to &amp;ldquo;I understand it&amp;rdquo; runs directly through that struggle.&lt;/p&gt;
&lt;p&gt;When AI absorbs that friction, it can produce correct output while bypassing the process entirely. You get the answer without the understanding. And the next time you hit a similar problem, you prompt again, because you never built the map.&lt;/p&gt;
&lt;p&gt;The question isn&amp;rsquo;t whether AI is useful. It clearly is. The question is what you&amp;rsquo;re trading when you let it do more and more of the thinking.&lt;/p&gt;
&lt;h2 id="what-copilot-taught-me-about-myself"&gt;What Copilot taught me about myself
&lt;/h2&gt;&lt;p&gt;The most honest data point I have is this: I considered cancelling my Copilot subscription because I noticed I was becoming lazy and less focused.&lt;/p&gt;
&lt;p&gt;Lazy is a broad word, so let me be specific. What I noticed was a change in how I started problems. Before: I&amp;rsquo;d read through the context, form a rough approach in my head, maybe sketch something out. After a few months with Copilot: I found myself opening a file and almost immediately waiting for a suggestion, before I&amp;rsquo;d fully thought about what I wanted to do. The autocomplete had become a first instinct, not a second one.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s a subtle but meaningful shift. It&amp;rsquo;s not that the suggestions were wrong. Often they were fine. It&amp;rsquo;s that the act of reaching for them had started to replace the act of thinking. My attention was shorter. I was less likely to sit with a problem before moving. More passive in a way I hadn&amp;rsquo;t consciously chosen.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;re still producing. The output looks fine. But the cognitive mode is different, and over time that adds up.&lt;/p&gt;
&lt;h2 id="the-risk-scales-with-your-foundation"&gt;The risk scales with your foundation
&lt;/h2&gt;&lt;p&gt;This is where I think the conversation gets more serious: the effect isn&amp;rsquo;t the same for everyone.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re a senior engineer with years of context, AI can be a genuine amplifier. You know what good looks like. You can evaluate suggestions critically. You catch the subtle mistakes. You ask better questions. The tool accelerates you because you already have the foundation to steer it.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re still building that foundation, the dynamic is different. The productive struggle I mentioned earlier is not just useful, it&amp;rsquo;s almost irreplaceable at that stage. The process of debugging something from scratch, of understanding why a design decision matters, of recognizing a bad pattern before it becomes a problem: those capabilities come from having done the work, not from having seen the output.&lt;/p&gt;
&lt;p&gt;I think AI used carelessly by junior developers doesn&amp;rsquo;t just slow their growth. It can actively shape them away from the kind of deep understanding that makes someone genuinely capable rather than just productive-looking.&lt;/p&gt;
&lt;h2 id="the-real-risk-isnt-replacement"&gt;The real risk isn&amp;rsquo;t replacement
&lt;/h2&gt;&lt;p&gt;There&amp;rsquo;s a lot of conversation about AI replacing developers. I think that framing misses something more immediate.&lt;/p&gt;
&lt;p&gt;The risk, at least for me, isn&amp;rsquo;t that AI takes my job. It&amp;rsquo;s that I let it take my &lt;strong&gt;competence&lt;/strong&gt; (the accumulated judgment, pattern recognition, and contextual understanding that makes a senior engineer different from someone who can prompt their way to a working prototype).&lt;/p&gt;
&lt;p&gt;AI rewards people who already know what good looks like. It accelerates insight when you have the foundation to evaluate what it gives you. When you don&amp;rsquo;t, or when you stop maintaining that foundation, you become dependent on a system you can&amp;rsquo;t fully reason about.&lt;/p&gt;
&lt;h2 id="what-intentional-use-actually-looks-like"&gt;What intentional use actually looks like
&lt;/h2&gt;&lt;p&gt;I didn&amp;rsquo;t cancel Copilot. But I did change how I use it.&lt;/p&gt;
&lt;p&gt;The shift was simple: I try to form my own approach first, before I ask AI anything. Not just a vague direction, a specific enough idea that I could start without it. Then I use AI to accelerate, validate, or challenge that approach. Sometimes it shows me a better path. Sometimes I find I&amp;rsquo;d already gotten there. Either way, I&amp;rsquo;ve done the thinking.&lt;/p&gt;
&lt;p&gt;I also try to be deliberate about when I let AI drive and when I don&amp;rsquo;t. Boilerplate, repetitive patterns, documentation: fine. Logic I don&amp;rsquo;t fully understand yet, architectural decisions, anything I&amp;rsquo;d struggle to explain to someone else afterwards: I work through those myself first.&lt;/p&gt;
&lt;p&gt;The distinction I keep coming back to: are you using AI to &lt;strong&gt;extend&lt;/strong&gt; your capabilities, or to &lt;strong&gt;externalize&lt;/strong&gt; them? One makes you better over time. The other quietly makes you less capable than you were.&lt;/p&gt;
&lt;p&gt;What truly makes the difference as a software engineer is the ability to craft and package a tailored solution for each specific context, not just to follow the latest tool, trend, or suggestion. That judgment doesn&amp;rsquo;t come from prompts; it comes from deliberate practice and honest reflection about what you actually understand.&lt;/p&gt;
&lt;p&gt;The question is worth asking regularly, especially when the output looks good and everything feels fast.&lt;/p&gt;
&lt;p&gt;Are you extending yourself, or outsourcing yourself?&lt;/p&gt;</description></item><item><title>Adding Interactive Cockpit Controls to Grav-Sync</title><link>https://bstz.it/p/adding-interactive-cockpit-controls-to-grav-sync/</link><pubDate>Sun, 09 Nov 2025 00:00:00 +0000</pubDate><guid>https://bstz.it/p/adding-interactive-cockpit-controls-to-grav-sync/</guid><description>&lt;p&gt;Today I implemented a major visual upgrade to my GitHub Game Off 2025 project, &lt;strong&gt;Grav-Sync&lt;/strong&gt;, by adding an interactive cockpit interface with animated control knobs. This enhancement significantly improves the game&amp;rsquo;s visual feedback and makes the controls more intuitive and engaging.&lt;/p&gt;
&lt;h2 id="what-changed"&gt;What Changed
&lt;/h2&gt;&lt;h3 id="animated-control-knobs"&gt;Animated Control Knobs
&lt;/h3&gt;&lt;p&gt;The main feature I added today is a fully functional cockpit interface with two animated control knobs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Red Knob&lt;/strong&gt; (controlled by A/D keys): Adjusts wave frequency&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Blue Knob&lt;/strong&gt; (controlled by W/S keys): Adjusts wave amplitude&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each knob has a 2-frame animation that advances whenever the player presses the corresponding control keys, providing immediate visual feedback for player input.&lt;/p&gt;
&lt;h3 id="new-sprite-system-method"&gt;New Sprite System Method
&lt;/h3&gt;&lt;p&gt;To support the knobs, I extended the &lt;code&gt;Sprite&lt;/code&gt; class with a new &lt;code&gt;nextFrame()&lt;/code&gt; method that allows manual frame advancement. This is different from the existing automatic animation used by the Earth sprite, which continuously cycles through frames. The knobs only animate when the player provides input, making them feel more responsive and connected to the controls.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Advance animation by one frame (useful for manual control)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="nx"&gt;nextFrame&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;len&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="input-handler-integration"&gt;Input Handler Integration
&lt;/h3&gt;&lt;p&gt;I updated the &lt;code&gt;InputHandler&lt;/code&gt; class to trigger knob animations alongside the existing player wave adjustments. Now when you press W/S or A/D keys, not only does your wave change, but the corresponding knob visibly rotates, providing clear visual feedback:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;pressed&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;keyIsDown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;87&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;keyIsDown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;119&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;increaseAmplitude&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;blueKnob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nextFrame&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="cockpit-rendering"&gt;Cockpit Rendering
&lt;/h3&gt;&lt;p&gt;I created a new &lt;code&gt;drawCockpit()&lt;/code&gt; method in the &lt;code&gt;Game&lt;/code&gt; class that handles the rendering of the cockpit base image and positions both knobs correctly relative to it. The cockpit is positioned near the bottom of the screen, and the knobs are placed at appropriate positions using a local coordinate system:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The cockpit base is rendered first&lt;/li&gt;
&lt;li&gt;Both knobs are positioned relative to the cockpit using &lt;code&gt;push()&lt;/code&gt;/&lt;code&gt;pop()&lt;/code&gt; transformations&lt;/li&gt;
&lt;li&gt;Each knob maintains its current animation frame and scale&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="ui-repositioning"&gt;UI Repositioning
&lt;/h3&gt;&lt;p&gt;To accommodate the new cockpit interface, I repositioned the UI elements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Moved amplitude and frequency displays to be adjacent to their respective knobs&lt;/li&gt;
&lt;li&gt;Centered the sync percentage and score displays on the cockpit&lt;/li&gt;
&lt;li&gt;Removed some redundant labels to keep the interface clean&lt;/li&gt;
&lt;li&gt;Adjusted the Earth sprite position slightly upward to leave room for the cockpit&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="configuration-adjustments"&gt;Configuration Adjustments
&lt;/h3&gt;&lt;p&gt;I made a small but important change to the configuration: the Earth sprite&amp;rsquo;s Y position was adjusted upward by 80 pixels (&lt;code&gt;config.earthY = config.screenHeight / 2 - 80&lt;/code&gt;) to better center the visible play area above the cockpit.&lt;/p&gt;
&lt;h2 id="technical-implementation"&gt;Technical Implementation
&lt;/h2&gt;&lt;p&gt;The implementation involved several files:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;animations/knob.json&lt;/strong&gt; - Animation configuration defining two frames for the knob rotation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;sprites/&lt;/strong&gt; - Added three new images: &lt;code&gt;cockpit.png&lt;/code&gt;, &lt;code&gt;red-knob.png&lt;/code&gt;, and &lt;code&gt;blue-knob.png&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;sketch.js&lt;/strong&gt; - Preloaded new assets and initialized knob sprites&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;game.js&lt;/strong&gt; - Implemented &lt;code&gt;drawCockpit()&lt;/code&gt; method for rendering&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;input.js&lt;/strong&gt; - Integrated knob animations with key press handlers&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;sprite.js&lt;/strong&gt; - Added &lt;code&gt;nextFrame()&lt;/code&gt; method for manual animation control&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ui.js&lt;/strong&gt; - Repositioned UI text elements&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;config.js&lt;/strong&gt; - Adjusted Earth position&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="code-organization"&gt;Code Organization
&lt;/h2&gt;&lt;p&gt;After implementing the cockpit feature, I also performed a minor refactoring to better organize global variables in &lt;code&gt;sketch.js&lt;/code&gt;, grouping related variables together for improved code readability.&lt;/p&gt;
&lt;h2 id="results"&gt;Results
&lt;/h2&gt;&lt;p&gt;&lt;img src="https://github.com/basteez/basteez.github.io/blob/main/content/post/2025-11-09-game-off-cockpit/result.gif?raw=true"
loading="lazy"
alt="result.gif"
&gt;&lt;/p&gt;
&lt;p&gt;The cockpit addition makes the game feel more polished and immersive. Players now have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Visual feedback&lt;/strong&gt; - See exactly when their inputs are registered&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Clearer controls&lt;/strong&gt; - The knobs make it obvious what W/S and A/D keys control&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Better aesthetics&lt;/strong&gt; - The cockpit theme reinforces the space/orbital mechanics concept&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Improved layout&lt;/strong&gt; - UI elements are better organized around the cockpit&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This change represents a significant step forward in the game&amp;rsquo;s visual design and user experience. The cockpit not only looks good but also serves a functional purpose by making the controls more intuitive and providing immediate visual feedback for player actions.&lt;/p&gt;
&lt;h2 id="whats-next"&gt;What&amp;rsquo;s Next
&lt;/h2&gt;&lt;p&gt;With the cockpit in place, the game is starting to feel more complete. Future improvements might include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Adding sound effects for knob rotation&lt;/li&gt;
&lt;li&gt;Adding a main manu&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can &lt;a class="link" href="https://github.com/basteez/grav-sync" target="_blank" rel="noopener"
&gt;check out the full code on GitHub&lt;/a&gt;, and I&amp;rsquo;d love to hear feedback from anyone who tries it out!&lt;/p&gt;</description></item><item><title>DevLog: Adding Dynamic Waves and Animated Sprites to Grav Sync</title><link>https://bstz.it/p/devlog-adding-dynamic-waves-and-animated-sprites-to-grav-sync/</link><pubDate>Sat, 08 Nov 2025 00:00:00 +0000</pubDate><guid>https://bstz.it/p/devlog-adding-dynamic-waves-and-animated-sprites-to-grav-sync/</guid><description>&lt;p&gt;Over the past development cycle, I worked on two features for Grav Sync, these updates transformed the game from a static puzzle into a more dynamic and visually engaging experience.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a detailed look at what was implemented and why.&lt;/p&gt;
&lt;h2 id="branch-1-random-wave---introducing-dynamic-challenge"&gt;Branch 1: &lt;code&gt;random-wave&lt;/code&gt; - Introducing Dynamic Challenge
&lt;/h2&gt;&lt;h3 id="the-problem"&gt;The Problem
&lt;/h3&gt;&lt;p&gt;The original game featured a fixed target wave that players needed to match. While functional, this meant that once players learned the solution, the game became repetitive. Every playthrough was essentially the same puzzle.&lt;/p&gt;
&lt;h3 id="the-solution-randomized-target-frequencies"&gt;The Solution: Randomized Target Frequencies
&lt;/h3&gt;&lt;p&gt;I implemented a system to randomize the target wave&amp;rsquo;s frequency at the start of each round, making every attempt unique and challenging.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key Changes:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Random Frequency Generation&lt;/strong&gt;: Added &lt;code&gt;getRandomInt(10, 30)&lt;/code&gt; function to generate target frequencies within a balanced range&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dynamic Target Updates&lt;/strong&gt;: The target frequency now regenerates each time the target wave resets (when it reaches the player&amp;rsquo;s orbital radius)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;UI Enhancement&lt;/strong&gt;: Added on-screen display of the current target frequency to give players clear feedback&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Implementation Details:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Initialize with random frequency in setup()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="nx"&gt;targetWave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frequency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getRandomInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Regenerate on wave reset
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;targetWave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseRadius&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;playerRadius&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sync&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;targetWave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frequency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getRandomInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// New random target
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt; &lt;span class="nx"&gt;targetWave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseRadius&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;380&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="refining-the-synchronization-system"&gt;Refining the Synchronization System
&lt;/h3&gt;&lt;p&gt;While implementing randomization, I also overhauled how synchronization is calculated. The original system treated frequency and amplitude equally, but through playtesting, it became clear that &lt;strong&gt;frequency matching is far more critical&lt;/strong&gt; to achieving visual alignment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The New Weighted System:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;80% weight on frequency matching&lt;/strong&gt;: Ensures players prioritize getting the wave count correct&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;20% weight on amplitude matching&lt;/strong&gt;: Fine-tuning for perfect alignment&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Phase ignored&lt;/strong&gt;: Simplified gameplay by removing phase as a factor, making the puzzle more approachable&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Technical Implementation:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;calculateSync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// Frequency score: compare numeric values directly (ignoring phase)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;freqRange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;maxFrequency&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;minFrequency&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;freqDiff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;targetWave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frequency&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;playerWave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;freqRange&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;freqScore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;freqDiff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// Amplitude score: compare amplitude values
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;ampRange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;maxAmplitude&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;minAmplitude&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;ampDiff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;targetWave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amplitude&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;playerWave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amplitude&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;ampRange&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;ampScore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;ampDiff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// Combine with 80:20 weighting
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;freqScore&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;ampScore&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This change made the game feel more intuitive, players could immediately see the impact of frequency adjustments while still needing to fine-tune amplitude for perfect scores.&lt;/p&gt;
&lt;h3 id="impact"&gt;Impact
&lt;/h3&gt;&lt;p&gt;The &lt;code&gt;random-wave&lt;/code&gt; branch transformed Grav Sync from a single-solution puzzle into a replayable game with procedural variation. Each round now presents a fresh challenge, and the weighted sync system provides clearer feedback about what matters most.&lt;/p&gt;
&lt;h2 id="branch-2-sprites---bringing-earth-to-life"&gt;Branch 2: &lt;code&gt;sprites&lt;/code&gt; - Bringing Earth to Life
&lt;/h2&gt;&lt;h3 id="the-vision"&gt;The Vision
&lt;/h3&gt;&lt;p&gt;The original Earth was rendered as a simple static circle with basic ellipse shapes for continents. While functional, it lacked the polish and visual interest the game deserved. I wanted Earth to feel like a living, rotating planet at the center of the cosmic waves.&lt;/p&gt;
&lt;h3 id="creating-the-sprite-system"&gt;Creating the Sprite System
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Step 1: Sprite Sheet Preparation&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I found a &lt;a class="link" href="https://opengameart.org/content/rotating-pixel-art-earth" target="_blank" rel="noopener"
&gt;94-frame sprite sheet&lt;/a&gt; showing Earth rotating (attribution has been made into the repo):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Format&lt;/strong&gt;: 480×480 PNG&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Layout&lt;/strong&gt;: 10×10 grid (10 frames per row for 9 rows, 4 frames in the final row)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Frame size&lt;/strong&gt;: 48×48 pixels per frame&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Animation style&lt;/strong&gt;: Flat earth texture scrolling across a circular mask&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Step 2: Frame Data Mapping&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Created &lt;code&gt;animations/earth.json&lt;/code&gt; to map all 94 frames with precise coordinates:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;span class="lnt"&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;frames&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;sprite-00&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;position&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;x&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;y&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;w&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;h&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;48&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// ... 93 more frames
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Step 3: Building the Sprite Class&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Implemented a reusable &lt;code&gt;Sprite&lt;/code&gt; class in &lt;code&gt;sprite.js&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Sprite&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;speed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;animation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Array of p5.Image objects
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;speed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Animation speed (frames per draw call)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Scaling factor
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Current frame index
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;len&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;imageMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CENTER&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scale&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;animate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Advance animation
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Key Features:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Flexible animation speed&lt;/strong&gt;: Random speed on initialization creates subtle variation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scalable sprites&lt;/strong&gt;: Can match any target size (scaled to 96×96 to match original Earth diameter)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Center-based positioning&lt;/strong&gt;: Uses &lt;code&gt;imageMode(CENTER)&lt;/code&gt; for intuitive placement&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Error handling&lt;/strong&gt;: Validates animation array to prevent crashes&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="integration-challenges"&gt;Integration Challenges
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Challenge 1: Variable Scope Issues&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Initial implementation had bugs where the animation array wasn&amp;rsquo;t properly declared:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Problem: &amp;#39;animation&amp;#39; not declared
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;frames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// ReferenceError
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;earth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Sprite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...);&lt;/span&gt; &lt;span class="c1"&gt;// &amp;#39;i&amp;#39; undefined here
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;animation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt; &lt;span class="c1"&gt;// Properly scoped
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt; &lt;span class="c1"&gt;// Load frames...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt; &lt;span class="nx"&gt;earth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Sprite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;earthX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;earthY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;spriteScale&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Challenge 2: Sprite Positioning&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Initially, sprites appeared in the top-left corner instead of centered. The issue was that p5.js&amp;rsquo;s &lt;code&gt;image()&lt;/code&gt; function defaults to corner-mode positioning.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Wrapped rendering in &lt;code&gt;push()&lt;/code&gt;/&lt;code&gt;pop()&lt;/code&gt; and set &lt;code&gt;imageMode(CENTER)&lt;/code&gt; to ensure sprites draw from their center point.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Challenge 3: Size Matching&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The original hand-drawn Earth had a diameter of 96px (radius 48), but the sprite frames were 48×48.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Calculate scale factor dynamically:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;spriteScale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;earthRadius&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 96 / 48 = 2.0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="nx"&gt;earth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Sprite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;earthX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;earthY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;spriteScale&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="the-result"&gt;The Result
&lt;/h3&gt;&lt;p&gt;&lt;img src="https://github.com/basteez/basteez.github.io/blob/main/content/post/2025-11-08-game-off-waves-and-sprites/earth.gif?raw=true"
loading="lazy"
alt="4.gif"
&gt;&lt;/p&gt;
&lt;p&gt;The sprite system brought immediate visual improvements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Living planet&lt;/strong&gt;: Earth now rotates continuously, adding life to the scene&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Variable speed&lt;/strong&gt;: Random animation speeds (0.1–0.4) create subtle uniqueness each game&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Smooth animation&lt;/strong&gt;: 94 frames provide fluid rotation without obvious looping&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Maintained aesthetics&lt;/strong&gt;: Scaled perfectly to match the original Earth size&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="code-quality-improvements"&gt;Code Quality Improvements
&lt;/h3&gt;&lt;p&gt;Added validation to prevent edge-case crashes:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;undefined&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Sprite: animation array is empty or invalid.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Also fixed a stroke bleed issue where wave rendering affected subsequent draws:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;drawWave&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wave&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currentTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// ... draw wave ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt; &lt;span class="nx"&gt;endShape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CLOSE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;black&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Reset stroke to prevent bleeding
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="whats-next"&gt;What&amp;rsquo;s Next?
&lt;/h2&gt;&lt;p&gt;With these two branches merged, Grav Sync now has:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dynamic, randomized gameplay&lt;/li&gt;
&lt;li&gt;Polished visual presentation&lt;/li&gt;
&lt;li&gt;Intuitive sync feedback&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Future improvements could include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Audio integration&lt;/strong&gt;: Frequency-based soundscapes using p5.sound&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Difficulty curves&lt;/strong&gt;: Progressive challenge scaling&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Particle effects or shaders&lt;/strong&gt;: Visual feedback when sync is achieved&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;These two updates made Grav Sync more engaging: random frequencies add replay value, and the animated Earth sprite improves the visual appeal. The game is now more fun to play than the original static version.&lt;/p&gt;
&lt;p&gt;You can &lt;a class="link" href="https://github.com/basteez/grav-sync" target="_blank" rel="noopener"
&gt;check out the full code on GitHub&lt;/a&gt;, and I&amp;rsquo;d love to hear feedback from anyone who tries it out!&lt;/p&gt;</description></item><item><title>DevLog #1 GravSync</title><link>https://bstz.it/p/devlog-%231-gravsync/</link><pubDate>Sun, 02 Nov 2025 00:00:00 +0000</pubDate><guid>https://bstz.it/p/devlog-%231-gravsync/</guid><description>&lt;p&gt;This year again, as every November since 2012, &lt;a class="link" href="https://itch.io/jam/game-off-2025" target="_blank" rel="noopener"
&gt;GitHub Game Off&lt;/a&gt; takes place, the game jam organized by GitHub where you have to develop a video game on a specific theme within a month.
This year I decided to participate with the excuse of practicing with JavaScript (specifically I chose to use &lt;a class="link" href="https://p5js.org/" target="_blank" rel="noopener"
&gt;p5.js&lt;/a&gt;, but this might change if I see it doesn&amp;rsquo;t quite suit my needs).&lt;/p&gt;
&lt;h2 id="the-theme"&gt;The theme
&lt;/h2&gt;&lt;p&gt;This year&amp;rsquo;s theme is &lt;strong&gt;waves&lt;/strong&gt; (quoting from the jam page)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;wave /weɪv/&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Noun&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A moving ridge on the surface of a liquid (for example, ocean waves or coffee ripples).&lt;/li&gt;
&lt;li&gt;A repeating pattern of motion, energy, or sound (for example, sound waves or radio waves).&lt;/li&gt;
&lt;li&gt;A sudden burst or surge (for example, a wave of enemies or a wave of emotion).&lt;/li&gt;
&lt;li&gt;A gesture of greeting or farewell.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Verb&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;To move your hand in greeting or farewell.&lt;/li&gt;
&lt;li&gt;To undulate or fluctuate (for example, wheat waving in the wind).&lt;/li&gt;
&lt;li&gt;To signal or summon (for example, waving someone through a door).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Adjective&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Flowing, wavy, or oscillating (for example, wavy hair).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Acronyms (OK, we made these up)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Wizards Accelerating Velocity Every Sunday&lt;/li&gt;
&lt;li&gt;Warriors Against Variable Energy Surges&lt;/li&gt;
&lt;li&gt;Whales And Vikings Engaging Submarines&lt;/li&gt;
&lt;li&gt;Or make up your own—we love seeing how creative you get with it!&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h2 id="my-idea"&gt;My idea
&lt;/h2&gt;&lt;p&gt;As a space and astrophysics enthusiast, the word &amp;ldquo;waves&amp;rdquo; immediately brought &lt;a class="link" href="https://en.wikipedia.org/wiki/Gravitational_wave" target="_blank" rel="noopener"
&gt;gravitational waves&lt;/a&gt; to mind, so why not create a mini puzzle game where you have to save Earth from malicious gravitational waves (whatever that means)?&lt;/p&gt;
&lt;p&gt;The idea is simple: The player tunes multiple gravitational waves to achieve resonance and stabilize the universe. The challenge is to synchronize the waves under time pressure while maintaining alignment.&lt;/p&gt;
&lt;p&gt;But how to implement it?&lt;/p&gt;
&lt;p&gt;Initially, since you need to make something small for a jam, I simply thought of showing two sine waves, the first being the target wave and the second controlled by the player, but that would have been incredibly boring, so why not put Earth at the center and make the waves &amp;ldquo;arrive&amp;rdquo; on it?&lt;/p&gt;
&lt;p&gt;So here&amp;rsquo;s planet Earth in all its splendor
&lt;img src="https://github.com/basteez/basteez.github.io/blob/main/content/post/2025-11-02-game-off-prototype/pale-blue-dot.png?raw=true"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;But how do you create a circular sine wave?
Searching a bit on the web, it turns out that the formula for drawing a sine wave on a circumference is this&lt;/p&gt;
&lt;p&gt;[
r(\theta) = r_0 + A \cdot \sin(n \theta + \phi)
]&lt;/p&gt;
&lt;p&gt;so having defined my JS object to represent the wave like this&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;targetWave&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;baseRadius&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;380&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;amplitude&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;frequency&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;phase&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;animationSpeed&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;I defined the function to draw it along a circumference like this&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;drawWave&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wave&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;wave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;wave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;wave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;strokeWeight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;noFill&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;beginShape&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;TWO_PI&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;phaseRadians&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;radians&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;phase&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;baseRadius&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;wave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseRadius&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;wave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amplitude&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;wave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frequency&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;phaseRadians&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;currentTime&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;wave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;animationSpeed&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;baseRadius&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;earthX&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;earthY&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;vertex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;endShape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CLOSE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;and here&amp;rsquo;s the result&lt;/p&gt;
&lt;p&gt;&lt;img src="https://github.com/basteez/basteez.github.io/blob/main/content/post/2025-11-02-game-off-prototype/1.gif?raw=true"
loading="lazy"
alt="1.gif"
&gt;&lt;/p&gt;
&lt;p&gt;At this point, however, it seemed more like the circumferences were rotating rather than being sine waves, so I decided to add some disturbance, but only for rendering purposes so as not to influence the player&amp;rsquo;s synchronization calculation.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;drawWave&lt;/code&gt; function therefore became like this&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;drawWave&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wave&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currentTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;wave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;wave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;wave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;strokeWeight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;noFill&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;beginShape&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;TWO_PI&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;phaseRadians&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;radians&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;phase&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// Base wave calculation (for sync)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;baseRadius&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;wave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseRadius&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;wave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amplitude&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;wave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frequency&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;phaseRadians&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;currentTime&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;wave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;animationSpeed&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// Add subtle disturbance for visual effect only
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;disturbance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;currentTime&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;currentTime&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;currentTime&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1.2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;currentTime&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;baseRadius&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;disturbance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;earthX&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;earthY&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;vertex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;endShape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CLOSE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img src="https://github.com/basteez/basteez.github.io/blob/main/content/post/2025-11-02-game-off-prototype/2.gif?raw=true"
loading="lazy"
alt="2.gif"
&gt;&lt;/p&gt;
&lt;p&gt;At this point, the last thing to do before closing the day was to make the target wave circumference reduce over time and once it reached the player&amp;rsquo;s circumference, calculate the score based on the sync value (for now I add a point to the score if sync &amp;gt; 80%, we&amp;rsquo;ll see later)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;targetWave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseRadius&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;targetWave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseRadius&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Reduce base radius by 0.5 each frame
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt; &lt;span class="nx"&gt;targetWave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseRadius&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targetWave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseRadius&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Don&amp;#39;t go below 50
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;targetWave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseRadius&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;playerWave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseRadius&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sync&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// make the target wave big again to simulate a second wave
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt; &lt;span class="nx"&gt;targetWave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseRadius&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;380&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img src="https://github.com/basteez/basteez.github.io/blob/main/content/post/2025-11-02-game-off-prototype/3.gif?raw=true"
loading="lazy"
alt="3.gif"
&gt;&lt;/p&gt;
&lt;p&gt;Obviously the player can use the keyboard to modify the amplitude and frequency of their own wave to align with the target wave&lt;/p&gt;
&lt;p&gt;&lt;img src="https://github.com/basteez/basteez.github.io/blob/main/content/post/2025-11-02-game-off-prototype/4.gif?raw=true"
loading="lazy"
alt="4.gif"
&gt;&lt;/p&gt;
&lt;h2 id="first-day-done"&gt;First day done
&lt;/h2&gt;&lt;p&gt;For now I&amp;rsquo;m satisfied, I&amp;rsquo;ve omitted the technical details on how I calculated the sync and other little things (forgive me, it&amp;rsquo;s my first devlog). If you want to take a look at the code it&amp;rsquo;s available &lt;a class="link" href="https://github.com/basteez/grav-sync" target="_blank" rel="noopener"
&gt;on my GitHub profile&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Code Smells: Learn to Sniff Them Out</title><link>https://bstz.it/p/code-smells-learn-to-sniff-them-out/</link><pubDate>Fri, 22 Nov 2024 00:00:00 +0000</pubDate><guid>https://bstz.it/p/code-smells-learn-to-sniff-them-out/</guid><description>&lt;p&gt;&amp;ldquo;Code smells&amp;rdquo; are subtle indicators of underlying problems in your codebase. They don’t crash your app or throw errors but can lead to long-term maintainability issues if left unaddressed. Let’s dive into &lt;strong&gt;what code smells are&lt;/strong&gt;, &lt;strong&gt;examples of common smells&lt;/strong&gt;, and &lt;strong&gt;how to clean them up.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="what-are-code-smells"&gt;What Are Code Smells?
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Coined by Martin Fowler in &lt;a class="link" href="https://amzn.eu/d/fxygxXO" target="_blank" rel="noopener"
&gt;&lt;em&gt;Refactoring: Improving the Design of Existing Code&lt;/em&gt;&lt;/a&gt;, code smells are symptoms of poor design or coding practices.&lt;/li&gt;
&lt;li&gt;They signal that &lt;strong&gt;something might need refactoring&lt;/strong&gt;, even if it technically works.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;🧠 &lt;strong&gt;Think of them as warning signs, not bugs.&lt;/strong&gt; A bug means something is broken; a smell means your design could use improvement.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="common-code-smells-and-how-to-fix-them"&gt;Common Code Smells and How to Fix Them
&lt;/h2&gt;&lt;h3 id="long-methods"&gt;Long Methods
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The Smell&lt;/strong&gt;: A single method tries to do too much (e.g., 200+ lines of code).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Why It’s Bad&lt;/strong&gt;: Hard to read, test, and debug.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt;: Break the method into smaller, single-purpose methods. Use meaningful names to make their purpose clear.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;processOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Code for validating order &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Code for applying discounts &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Code for calculating total &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Code for sending confirmation email }&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;After:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;processOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;validateOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;applyDiscounts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;calculateTotal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sendConfirmation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;hr&gt;
&lt;h3 id="large-classes-god-objects"&gt;Large Classes (God Objects)
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The Smell&lt;/strong&gt;: A class tries to handle too many responsibilities.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Why It’s Bad&lt;/strong&gt;: Violates the Single Responsibility Principle (SRP). Hard to maintain and extend.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt;: Split the class into smaller, more focused classes. Use patterns like &lt;strong&gt;composition&lt;/strong&gt; or &lt;strong&gt;delegation&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserManager&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;createUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;validateUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;sendEmail&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;logActivity&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;After:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;createUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="o"&gt;---&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserValidator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;validateUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="o"&gt;---&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EmailService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;sendEmail&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;hr&gt;
&lt;h3 id="magic-numbers-and-strings"&gt;Magic Numbers and Strings
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The Smell&lt;/strong&gt;: Unexplained hard-coded values scattered throughout your code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Why It’s Bad&lt;/strong&gt;: Makes code confusing and harder to maintain.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt;: Replace them with constants or enums.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;After:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;STATUS_APPROVED&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;STATUS_APPROVED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;hr&gt;
&lt;h3 id="duplicate-code"&gt;Duplicate Code
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The Smell&lt;/strong&gt;: Identical or nearly identical code blocks in multiple places.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Why It’s Bad&lt;/strong&gt;: Increases maintenance effort. Changes in one place need to be replicated everywhere.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt;: Abstract the duplicated logic into a reusable method or class.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;calculateDiscount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;calculateTax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;After:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;applyPercentage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;percentage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;percentage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;hr&gt;
&lt;h3 id="long-parameter-lists"&gt;Long Parameter Lists
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The Smell&lt;/strong&gt;: Methods or constructors take too many parameters.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Why It’s Bad&lt;/strong&gt;: Makes code harder to understand and prone to errors when calling.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt;: Use an object to encapsulate related parameters.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;createOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;customerName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;After:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;createOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OrderRequest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="tools-to-detect-code-smells"&gt;Tools to Detect Code Smells
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;a class="link" href="https://bstz.it/p/how-to-use-sonarqube-and-sonarscanner-locally-to-level-up-your-code-quality/" target="_blank" rel="noopener"
&gt;&lt;strong&gt;SonarQube&lt;/strong&gt;&lt;/a&gt;: Analyzes code quality and detects common smells.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IntelliJ IDEA&lt;/strong&gt;: Built-in inspections flag potential issues like long methods or God objects.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Refactoring Books&lt;/strong&gt;: &lt;a class="link" href="https://amzn.eu/d/fxygxXO" target="_blank" rel="noopener"
&gt;&lt;em&gt;Refactoring: Improving the Design of Existing Code&lt;/em&gt;&lt;/a&gt; by Martin Fowler is a must-read!&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="actionable-tips-to-avoid-code-smells"&gt;Actionable Tips to Avoid Code Smells
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Start Small&lt;/strong&gt;: Refactor one smell at a time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Embrace Code Reviews&lt;/strong&gt;: Fresh eyes can spot smells you’ve become blind to.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automate Tests&lt;/strong&gt;: Refactoring is less scary when you have a safety net of tests.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Follow SOLID Principles&lt;/strong&gt;: Adhering to these can prevent many smells in the first place.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;p&gt;💡 &lt;strong&gt;Takeaway&lt;/strong&gt;: Code smells are inevitable as your codebase grows, but catching and addressing them early can save you from costly rewrites later.&lt;/p&gt;</description></item><item><title>An In-Depth Look at Project Lombok: A Follow-Up 🚀</title><link>https://bstz.it/p/an-in-depth-look-at-project-lombok-a-follow-up/</link><pubDate>Wed, 20 Nov 2024 00:00:00 +0000</pubDate><guid>https://bstz.it/p/an-in-depth-look-at-project-lombok-a-follow-up/</guid><description>&lt;p&gt;Following up on &lt;a class="link" href="https://www.linkedin.com/posts/tiziano-basile-264681147_projectlombok-java-cleancode-activity-7262501993489846272-MjPY?utm_source=share&amp;amp;utm_medium=member_desktop" target="_blank" rel="noopener"
&gt;my earlier post about 𝗣𝗿𝗼𝗷𝗲𝗰𝘁 𝗟𝗼𝗺𝗯𝗼𝗸 on LinkedIn&lt;/a&gt;, let’s dive deeper into 𝘄𝗵𝘆 𝗟𝗼𝗺𝗯𝗼𝗸 𝗰𝗮𝗻 𝗯𝗲 𝗮 𝗯𝗹𝗲𝘀𝘀𝗶𝗻𝗴 𝗮𝗻𝗱 𝗮 𝗰𝘂𝗿𝘀𝗲 in your codebase.&lt;/p&gt;
&lt;h2 id="-𝗧𝗵𝗲-𝗚𝗼𝗼𝗱-𝗪𝗵𝘆-𝗟𝗼𝗺𝗯𝗼𝗸-𝗦𝗵𝗶𝗻𝗲𝘀"&gt;✅ 𝗧𝗵𝗲 𝗚𝗼𝗼𝗱: 𝗪𝗵𝘆 𝗟𝗼𝗺𝗯𝗼𝗸 𝗦𝗵𝗶𝗻𝗲𝘀
&lt;/h2&gt;&lt;h3 id="𝗖𝗹𝗲𝗮𝗻𝗲𝗿-𝗥𝗲𝗮𝗱𝗮𝗯𝗹𝗲-𝗖𝗼𝗱𝗲"&gt;𝗖𝗹𝗲𝗮𝗻𝗲𝗿, 𝗥𝗲𝗮𝗱𝗮𝗯𝗹𝗲 𝗖𝗼𝗱𝗲:
&lt;/h3&gt;&lt;p&gt;Lombok eliminates the need for repetitive methods like getters, setters, toString, and constructors.
With annotations like &lt;code&gt;@Getter&lt;/code&gt;, &lt;code&gt;@Setter&lt;/code&gt;, &lt;code&gt;@Builder&lt;/code&gt;, and &lt;code&gt;@Data&lt;/code&gt;, classes become concise and easier to understand.&lt;/p&gt;
&lt;h3 id="𝗙𝗮𝘀𝘁𝗲𝗿-𝗗𝗲𝘃𝗲𝗹𝗼𝗽𝗺𝗲𝗻𝘁"&gt;𝗙𝗮𝘀𝘁𝗲𝗿 𝗗𝗲𝘃𝗲𝗹𝗼𝗽𝗺𝗲𝗻𝘁:
&lt;/h3&gt;&lt;p&gt;Focus on 𝘄𝗵𝗮𝘁 𝗺𝗮𝘁𝘁𝗲𝗿𝘀—business logic—while Lombok handles the repetitive tasks.&lt;/p&gt;
&lt;h3 id="𝗜𝗺𝗽𝗿𝗼𝘃𝗲𝗱-𝗠𝗮𝗶𝗻𝘁𝗮𝗶𝗻𝗮𝗯𝗶𝗹𝗶𝘁𝘆"&gt;𝗜𝗺𝗽𝗿𝗼𝘃𝗲𝗱 𝗠𝗮𝗶𝗻𝘁𝗮𝗶𝗻𝗮𝗯𝗶𝗹𝗶𝘁𝘆
&lt;/h3&gt;&lt;p&gt;Changes to class fields automatically reflect in generated methods, reducing manual errors.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;@𝗘𝗾𝘂𝗮𝗹𝘀𝗔𝗻𝗱𝗛𝗮𝘀𝗵𝗖𝗼𝗱𝗲: No more tedious 𝘩𝘢𝘴𝘩𝘊𝘰𝘥𝘦 and 𝘦𝘲𝘶𝘢𝘭𝘴 implementations.&lt;/li&gt;
&lt;li&gt;@𝗕𝘂𝗶𝗹𝗱𝗲𝗿: Simplifies complex object creation with immutability.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="𝗕𝗲𝘁𝘁𝗲𝗿-𝗖𝗼𝗱𝗲-𝗥𝗲𝘃𝗶𝗲𝘄𝘀"&gt;𝗕𝗲𝘁𝘁𝗲𝗿 𝗖𝗼𝗱𝗲 𝗥𝗲𝘃𝗶𝗲𝘄𝘀:
&lt;/h3&gt;&lt;p&gt;Git diffs become cleaner since boilerplate is hidden. 𝗧𝗲𝗮𝗺𝘀 𝗰𝗮𝗻 𝘀𝗽𝗼𝘁 𝗺𝗲𝗮𝗻𝗶𝗻𝗴𝗳𝘂𝗹 𝗰𝗵𝗮𝗻𝗴𝗲𝘀 𝗳𝗮𝘀𝘁𝗲𝗿.&lt;/p&gt;
&lt;h2 id="-𝗧𝗵𝗲-𝗥𝗶𝘀𝗸𝘀-𝗮𝗻𝗱-𝗖𝗮𝘃𝗲𝗮𝘁𝘀-𝗼𝗳-𝗟𝗼𝗺𝗯𝗼𝗸"&gt;❌ 𝗧𝗵𝗲 𝗥𝗶𝘀𝗸𝘀 𝗮𝗻𝗱 𝗖𝗮𝘃𝗲𝗮𝘁𝘀 𝗼𝗳 𝗟𝗼𝗺𝗯𝗼𝗸
&lt;/h2&gt;&lt;h3 id="𝗧𝗿𝗮𝗻𝘀𝗽𝗮𝗿𝗲𝗻𝗰𝘆-𝗜𝘀𝘀𝘂𝗲𝘀"&gt;𝗧𝗿𝗮𝗻𝘀𝗽𝗮𝗿𝗲𝗻𝗰𝘆 𝗜𝘀𝘀𝘂𝗲𝘀:
&lt;/h3&gt;&lt;p&gt;Generated code isn’t visible in your editor unless you delve into decompiled classes.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Can confuse 𝗻𝗲𝘄 𝘁𝗲𝗮𝗺 𝗺𝗲𝗺𝗯𝗲𝗿𝘀 or those unfamiliar with Lombok.&lt;/li&gt;
&lt;li&gt;Debugging becomes harder when stack traces point to 𝗶𝗻𝘃𝗶𝘀𝗶𝗯𝗹𝗲 𝗺𝗲𝘁𝗵𝗼𝗱𝘀.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="𝗢𝘃𝗲𝗿𝘂𝘀𝗲-𝗼𝗳-𝗔𝗻𝗻𝗼𝘁𝗮𝘁𝗶𝗼𝗻𝘀"&gt;𝗢𝘃𝗲𝗿𝘂𝘀𝗲 𝗼𝗳 𝗔𝗻𝗻𝗼𝘁𝗮𝘁𝗶𝗼𝗻𝘀:
&lt;/h3&gt;&lt;p&gt;Lombok can lead to 𝗰𝗼𝗱𝗲 𝗯𝗹𝗼𝗮𝘁 if misused:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;@Data&lt;/strong&gt; generates &lt;code&gt;equals&lt;/code&gt;, &lt;code&gt;hashCode&lt;/code&gt;, &lt;code&gt;toString&lt;/code&gt;, getters, setters, and a constructor—some of which might not be needed.&lt;/li&gt;
&lt;li&gt;This can impact 𝗽𝗲𝗿𝗳𝗼𝗿𝗺𝗮𝗻𝗰𝗲 or cause 𝘂𝗻𝗲𝘅𝗽𝗲𝗰𝘁𝗲𝗱 𝗯𝗲𝗵𝗮𝘃𝗶𝗼𝗿 (i.e @𝘛𝘰𝘚𝘵𝘳𝘪𝘯𝘨 in nested entities could easily lead to a 𝗦𝘁𝗮𝗰𝗸𝗢𝘃𝗲𝗿𝗙𝗹𝗼𝘄𝗘𝘅𝗰𝗲𝗽𝘁𝗶𝗼𝗻).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="𝗧𝗼𝗼𝗹𝗶𝗻𝗴-𝗮𝗻𝗱-𝗖𝗼𝗺𝗽𝗮𝘁𝗶𝗯𝗶𝗹𝗶𝘁𝘆"&gt;𝗧𝗼𝗼𝗹𝗶𝗻𝗴 𝗮𝗻𝗱 𝗖𝗼𝗺𝗽𝗮𝘁𝗶𝗯𝗶𝗹𝗶𝘁𝘆:
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;IDEs or static analysis tools sometimes struggle with Lombok-generated code.&lt;/li&gt;
&lt;li&gt;Upgrading to new Java versions may introduce 𝗰𝗼𝗺𝗽𝗮𝘁𝗶𝗯𝗶𝗹𝗶𝘁𝘆 𝗶𝘀𝘀𝘂𝗲𝘀.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="𝗗𝗲𝗽𝗲𝗻𝗱𝗲𝗻𝗰𝘆-𝗟𝗼𝗰𝗸-𝗜𝗻"&gt;𝗗𝗲𝗽𝗲𝗻𝗱𝗲𝗻𝗰𝘆 𝗟𝗼𝗰𝗸-𝗜𝗻:
&lt;/h3&gt;&lt;p&gt;Removing Lombok later can be challenging, requiring significant rewrites.&lt;/p&gt;
&lt;h3 id="𝗗𝗲𝗯𝘂𝗴𝗴𝗶𝗻𝗴-𝗛𝗲𝗮𝗱𝗮𝗰𝗵𝗲𝘀"&gt;𝗗𝗲𝗯𝘂𝗴𝗴𝗶𝗻𝗴 𝗛𝗲𝗮𝗱𝗮𝗰𝗵𝗲𝘀:
&lt;/h3&gt;&lt;p&gt;Lombok-generated code may not have clear stack traces, making debugging non-trivial.&lt;/p&gt;
&lt;h2 id="𝗛𝗼𝘄-𝘁𝗼-𝗨𝘀𝗲-𝗟𝗼𝗺𝗯𝗼𝗸-𝗘𝗳𝗳𝗲𝗰𝘁𝗶𝘃𝗲𝗹𝘆"&gt;𝗛𝗼𝘄 𝘁𝗼 𝗨𝘀𝗲 𝗟𝗼𝗺𝗯𝗼𝗸 𝗘𝗳𝗳𝗲𝗰𝘁𝗶𝘃𝗲𝗹𝘆
&lt;/h2&gt;&lt;h3 id="𝗕𝗲-𝗦𝗲𝗹𝗲𝗰𝘁𝗶𝘃𝗲-𝘄𝗶𝘁𝗵-𝗔𝗻𝗻𝗼𝘁𝗮𝘁𝗶𝗼𝗻𝘀"&gt;𝗕𝗲 𝗦𝗲𝗹𝗲𝗰𝘁𝗶𝘃𝗲 𝘄𝗶𝘁𝗵 𝗔𝗻𝗻𝗼𝘁𝗮𝘁𝗶𝗼𝗻𝘀:
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Use specific annotations like @𝘎𝘦𝘵𝘵𝘦𝘳 and @𝘚𝘦𝘵𝘵𝘦𝘳 instead of all-in-one annotations like @𝘋𝘢𝘵𝘢.&lt;/li&gt;
&lt;li&gt;Avoid unnecessary methods (e.g., don’t generate 𝘦𝘲𝘶𝘢𝘭𝘴/𝘩𝘢𝘴𝘩𝘊𝘰𝘥𝘦 unless required).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="𝗨𝗻𝗱𝗲𝗿𝘀𝘁𝗮𝗻𝗱-𝘁𝗵𝗲-𝗚𝗲𝗻𝗲𝗿𝗮𝘁𝗲𝗱-𝗖𝗼𝗱𝗲"&gt;𝗨𝗻𝗱𝗲𝗿𝘀𝘁𝗮𝗻𝗱 𝘁𝗵𝗲 𝗚𝗲𝗻𝗲𝗿𝗮𝘁𝗲𝗱 𝗖𝗼𝗱𝗲:
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Use IDE features like decompile or delombok during builds to view generated code.&lt;/li&gt;
&lt;li&gt;Knowing what’s happening under the hood prevents surprises.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="𝗣𝗹𝗮𝗻-𝗳𝗼𝗿-𝗟𝗼𝗻𝗴-𝗧𝗲𝗿𝗺-𝗠𝗮𝗶𝗻𝘁𝗲𝗻𝗮𝗻𝗰𝗲"&gt;𝗣𝗹𝗮𝗻 𝗳𝗼𝗿 𝗟𝗼𝗻𝗴-𝗧𝗲𝗿𝗺 𝗠𝗮𝗶𝗻𝘁𝗲𝗻𝗮𝗻𝗰𝗲:
&lt;/h3&gt;&lt;p&gt;If you outgrow Lombok, a 𝗱𝗲𝗹𝗼𝗺𝗯𝗼𝗸 build step can help with the migration.&lt;/p&gt;
&lt;h3 id="conclusion"&gt;Conclusion
&lt;/h3&gt;&lt;p&gt;𝗟𝗼𝗺𝗯𝗼𝗸 𝗶𝘀 𝗮 𝗽𝗼𝘄𝗲𝗿𝗳𝘂𝗹 𝘁𝗼𝗼𝗹, 𝗯𝘂𝘁 𝗶𝘁 𝗿𝗲𝗾𝘂𝗶𝗿𝗲𝘀 𝗿𝗲𝘀𝗽𝗼𝗻𝘀𝗶𝗯𝗹𝗲 𝘂𝘀𝗮𝗴𝗲. It’s a must-have for teams looking to streamline development, but only if the team is aligned and mindful of the caveats.
What’s your experience with Lombok? Are you using it effectively in your projects? Let’s discuss! 🧑💻&lt;/p&gt;</description></item><item><title>How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality</title><link>https://bstz.it/p/how-to-use-sonarqube-and-sonarscanner-locally-to-level-up-your-code-quality/</link><pubDate>Wed, 13 Nov 2024 00:00:00 +0000</pubDate><guid>https://bstz.it/p/how-to-use-sonarqube-and-sonarscanner-locally-to-level-up-your-code-quality/</guid><description>&lt;p&gt;Code quality tools can make a huge difference in improving your coding skills by helping you identify code smells, bugs, and potential vulnerabilities.&lt;/p&gt;
&lt;p&gt;In this guide, we’ll explore how to set up &lt;strong&gt;SonarQube&lt;/strong&gt; and &lt;strong&gt;SonarScanner&lt;/strong&gt; locally. This allows you to analyze your code for potential improvements right on your machine.&lt;/p&gt;
&lt;h2 id="step-1-setting-up-sonarqube-with-docker"&gt;Step 1: Setting Up SonarQube with Docker
&lt;/h2&gt;&lt;p&gt;First, ensure you have Docker installed. With Docker, getting SonarQube up and running is straightforward:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Pull the SonarQube image&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;docker pull sonarqube&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Run SonarQube&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;docker run -d --name sonarqube -p 9000:9000 -p 9092:9092 sonarqube&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This command runs SonarQube in the background, mapping port &lt;code&gt;9000&lt;/code&gt; (for the SonarQube web interface) and &lt;code&gt;9092&lt;/code&gt; (optional).&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="step-2-accessing-sonarqube"&gt;Step 2: Accessing SonarQube
&lt;/h2&gt;&lt;p&gt;With SonarQube running, open your browser and go to &lt;a class="link" href="http://localhost:9000" target="_blank" rel="noopener"
&gt;http://localhost:9000&lt;/a&gt;. Enter the default credentials:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Username&lt;/strong&gt;: &lt;code&gt;admin&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Password&lt;/strong&gt;: &lt;code&gt;admin&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once logged in, you will be prompted to change the default password.&lt;/p&gt;
&lt;h2 id="step-3-generate-an-authentication-token"&gt;Step 3: Generate an Authentication Token
&lt;/h2&gt;&lt;p&gt;To allow SonarScanner to connect to SonarQube, you need to create an authentication token.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Click the &lt;strong&gt;A&lt;/strong&gt; icon in the top-right corner and select &lt;strong&gt;My Account&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Navigate to the &lt;strong&gt;Security&lt;/strong&gt; tab and click &lt;strong&gt;Generate a token&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Name your token (it can be user-specific or global) and save it somewhere secure, as it will only be displayed once.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="step-4-configure-sonar-projectproperties"&gt;Step 4: Configure &lt;code&gt;sonar-project.properties&lt;/code&gt;
&lt;/h2&gt;&lt;p&gt;Next, set up a configuration file to define key project properties SonarQube will use to analyze your code. In the root directory of your project, create a file named &lt;code&gt;sonar-project.properties&lt;/code&gt; and add the following content:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="l"&gt;sonar.projectKey=my:project&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Must be unique &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="l"&gt;sonar.projectName=my project name &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="l"&gt;sonar.projectVersion=1.0 sonar.sources=src/main/java&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Adjust based on your source directory &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="l"&gt;sonar.java.binaries=target/classes &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Adjust based on your compiled classes &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="l"&gt;sonar.tests=src/test/java&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Adjust based on your test directory&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This configuration file tells SonarQube about the structure and setup of your project.&lt;/p&gt;
&lt;h2 id="step-5-run-sonarscanner"&gt;Step 5: Run SonarScanner
&lt;/h2&gt;&lt;p&gt;With everything set up, it’s time to analyze your project. Open a terminal at the root of your project and execute the following command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;span class="lnt"&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mvn clean install &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="se"&gt;&lt;/span&gt;mvn dependency:copy-dependencies &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="se"&gt;&lt;/span&gt;docker run &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="se"&gt;&lt;/span&gt; --rm &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="se"&gt;&lt;/span&gt; --network host &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="se"&gt;&lt;/span&gt; -e &lt;span class="nv"&gt;SONAR_HOST_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;http://{YOUR LOCAL IP}:9000&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="se"&gt;&lt;/span&gt; -e &lt;span class="nv"&gt;SONAR_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{YOUR SONARQUBE TOKEN}&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="se"&gt;&lt;/span&gt; -v &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;:/usr/src&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="se"&gt;&lt;/span&gt; sonarsource/sonar-scanner-cli
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This command does the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cleans and builds your project using Maven (&lt;code&gt;mvn clean install&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Copies dependencies needed for analysis.&lt;/li&gt;
&lt;li&gt;Runs SonarScanner in a Docker container and connects it to your local SonarQube instance.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Replace &lt;code&gt;{YOUR LOCAL IP}&lt;/code&gt; with your machine’s local IP address and &lt;code&gt;{YOUR SONARQUBE TOKEN}&lt;/code&gt; with the token you generated in Step 4.&lt;/p&gt;
&lt;h2 id="step-6-review-the-analysis-results-in-sonarqube"&gt;Step 6: Review the Analysis Results in SonarQube
&lt;/h2&gt;&lt;p&gt;Once SonarScanner completes its run, return to &lt;a class="link" href="http://localhost:9000" target="_blank" rel="noopener"
&gt;http://localhost:9000&lt;/a&gt; and navigate to your project dashboard. Here, you’ll see a detailed report on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Code smells&lt;/strong&gt;: Areas of the codebase that could benefit from refactoring.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bugs&lt;/strong&gt;: Logical errors or anomalies in the code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vulnerabilities&lt;/strong&gt;: Security-related issues.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;Setting up SonarQube and SonarScanner locally allows you to take your code quality analysis into your own hands. Regularly reviewing these reports can help you develop better habits, improve your understanding of code quality principles, and ultimately level up your coding skills.&lt;/p&gt;</description></item><item><title>Design Patterns: Singleton</title><link>https://bstz.it/p/design-patterns-singleton/</link><pubDate>Wed, 04 Sep 2024 00:00:00 +0000</pubDate><guid>https://bstz.it/p/design-patterns-singleton/</guid><description>&lt;p&gt;&lt;strong&gt;Singleton&lt;/strong&gt; is a creational design pattern that ensures a class has only one instance and provides a global access point to that instance.&lt;/p&gt;
&lt;h2 id="problem"&gt;Problem
&lt;/h2&gt;&lt;p&gt;In software development, it&amp;rsquo;s common to need a single instance of a class to be shared across an application. Having multiple instances can lead to issues such as unexpected behaviors or inefficient resource usage.&lt;/p&gt;
&lt;h2 id="solution"&gt;Solution
&lt;/h2&gt;&lt;p&gt;The &lt;strong&gt;Singleton&lt;/strong&gt; pattern addresses this problem by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Making the class constructor private to prevent direct instantiation from outside the class.&lt;/li&gt;
&lt;li&gt;Providing a static public method that acts as a constructor. Before creating a new instance, it checks if an instance already exists in a private static field. If the field is &lt;code&gt;null&lt;/code&gt;, it creates a new instance and assigns it to the field; otherwise, it returns the existing instance.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://bstz.it/p/design-patterns-singleton/SingletonDiagram.svg"
loading="lazy"
alt="SingletonDiagram.svg"
&gt;&lt;/p&gt;
&lt;h2 id="java-example"&gt;Java example
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Singleton&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Singleton&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Singleton&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// initialization code&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Singleton&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Singleton&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="applications"&gt;Applications
&lt;/h2&gt;&lt;p&gt;Use the &lt;strong&gt;Singleton&lt;/strong&gt; pattern when you need a single instance of a class to be shared across the entire application.&lt;/p&gt;
&lt;h2 id="tips"&gt;Tips
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Be careful when using the &lt;strong&gt;Singleton&lt;/strong&gt; pattern in multi-threaded scenarios, as it is not inherently thread-safe. Check &lt;a class="link" href="https://bstz.it/p/singleton-pattern-thread-safety-in-java/" target="_blank" rel="noopener"
&gt;this article&lt;/a&gt; for further details&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Singleton pattern thread-safety in java</title><link>https://bstz.it/p/singleton-pattern-thread-safety-in-java/</link><pubDate>Wed, 04 Sep 2024 00:00:00 +0000</pubDate><guid>https://bstz.it/p/singleton-pattern-thread-safety-in-java/</guid><description>&lt;p&gt;As stated in &lt;a class="link" href="https://bstz.it/p/design-patterns-singleton/" target="_blank" rel="noopener"
&gt;this article&lt;/a&gt;, &lt;strong&gt;singleton&lt;/strong&gt; is not thread-safe by default. Let&amp;rsquo;s see how to make it thread-safe.&lt;/p&gt;
&lt;h2 id="basic-singleton-class"&gt;Basic singleton class
&lt;/h2&gt;&lt;p&gt;First, let&amp;rsquo;s recap how to create a basic singleton:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Singleton&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Singleton&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Singleton&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// initialization code&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Singleton&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Singleton&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This implementation does not guarantee correct behavior in a multithreaded environment.&lt;/p&gt;
&lt;h2 id="how-to-make-a-singleton-thread-safe"&gt;How to make a singleton thread-safe
&lt;/h2&gt;&lt;p&gt;We can achieve &lt;strong&gt;singleton thread-safety&lt;/strong&gt; in several ways:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Eager initialization&lt;/li&gt;
&lt;li&gt;Lazy initialization with method synchronization&lt;/li&gt;
&lt;li&gt;Double-checked initialization with synchronized block and volatile variable&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="eager-initialization"&gt;Eager initialization
&lt;/h3&gt;&lt;p&gt;In this approach, the instance is created when the class itself gets loaded in the application context to ensure thread-safety without relying on any synchronization mechanism.
However, it could lead to a waste of resources if the instance is never used, and it doesn’t allow passing parameters during instantiation.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EagerSingleton&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Instance created at the time of class loading&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EagerSingleton&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EagerSingleton&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;EagerSingleton&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EagerSingleton&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="lazy-initialization-with-method-synchronization"&gt;Lazy initialization with method synchronization
&lt;/h3&gt;&lt;p&gt;This approach ensures thread safety by synchronizing the &lt;code&gt;getInstance()&lt;/code&gt; method. However, this method can suffer from performance issues due to synchronization overhead on every call to &lt;code&gt;getInstance()&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SynchronizedSingleton&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SynchronizedSingleton&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;SynchronizedSingleton&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Synchronized method to control simultaneous access&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;synchronized&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SynchronizedSingleton&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SynchronizedSingleton&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="double-checked-initialization-with-synchronized-block-and-volatile-variable"&gt;Double-checked initialization with synchronized block and volatile variable
&lt;/h3&gt;&lt;p&gt;In this approach, similar the previous one, the synchronization overhead is reduced by synchronizing only the first time the instance is accessed.
The instance variable is marked as &lt;code&gt;volatile&lt;/code&gt; to prevent the occurrence of partially constructed instances.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DoubleCheckedSingleton&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// volatile keyword ensures that multiple threads handle the instance variable correctly&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;volatile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DoubleCheckedSingleton&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;DoubleCheckedSingleton&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Method to return the singleton instance with double-checked locking&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DoubleCheckedSingleton&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// First check without synchronization&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;synchronized&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DoubleCheckedSingleton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Second check with synchronization&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DoubleCheckedSingleton&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>Design Patterns: Facade</title><link>https://bstz.it/p/design-patterns-facade/</link><pubDate>Tue, 03 Sep 2024 00:00:00 +0000</pubDate><guid>https://bstz.it/p/design-patterns-facade/</guid><description>&lt;p&gt;Facade is a &lt;strong&gt;structural design pattern&lt;/strong&gt; that let&amp;rsquo;s you interact with complex systems through a simpler interface.&lt;/p&gt;
&lt;h2 id="problem"&gt;Problem
&lt;/h2&gt;&lt;p&gt;In software development, is often necessary to interact with complex systems composed of different interdependent classes and usually you don&amp;rsquo;t need to use all of them, but only a small subset.
You probably also want to avoid tight coupling with those classes as it would make difficult to make future changes.&lt;/p&gt;
&lt;h2 id="solution"&gt;Solution
&lt;/h2&gt;&lt;p&gt;This is where the &lt;strong&gt;Facade&lt;/strong&gt; pattern proves useful.
By providing a unified interface, the facade allows clients to interact with the system using only the subset of functionality they need, while hiding the complexities of the underlying subsystem.
This enhances both decoupling and simplicity.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://bstz.it/p/design-patterns-facade/FacadeDiagram.svg"
loading="lazy"
alt="FacadeDiagram.svg"
&gt;&lt;/p&gt;
&lt;h2 id="real-world-example"&gt;Real-World example
&lt;/h2&gt;&lt;p&gt;Imagine a software that uses different logging mechanisms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;File Logging&lt;/strong&gt; for saving logs to a file.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Console Logging&lt;/strong&gt; for outputting logs to the console.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Remote Server Logging&lt;/strong&gt; for sending logs to a remote server.
Each logging mechanism might involve different classes and methods. Managing and using all these logging mechanisms directly can be complex and cumbersome.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can use the &lt;strong&gt;Facade Pattern&lt;/strong&gt; to provide a simplified interface for logging.
The &lt;strong&gt;facade&lt;/strong&gt; will handle the complexities of interacting with different logging mechanisms and provide a unified way to log messages.&lt;/p&gt;
&lt;h2 id="java-example"&gt;Java example
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;span class="lnt"&gt;43
&lt;/span&gt;&lt;span class="lnt"&gt;44
&lt;/span&gt;&lt;span class="lnt"&gt;45
&lt;/span&gt;&lt;span class="lnt"&gt;46
&lt;/span&gt;&lt;span class="lnt"&gt;47
&lt;/span&gt;&lt;span class="lnt"&gt;48
&lt;/span&gt;&lt;span class="lnt"&gt;49
&lt;/span&gt;&lt;span class="lnt"&gt;50
&lt;/span&gt;&lt;span class="lnt"&gt;51
&lt;/span&gt;&lt;span class="lnt"&gt;52
&lt;/span&gt;&lt;span class="lnt"&gt;53
&lt;/span&gt;&lt;span class="lnt"&gt;54
&lt;/span&gt;&lt;span class="lnt"&gt;55
&lt;/span&gt;&lt;span class="lnt"&gt;56
&lt;/span&gt;&lt;span class="lnt"&gt;57
&lt;/span&gt;&lt;span class="lnt"&gt;58
&lt;/span&gt;&lt;span class="lnt"&gt;59
&lt;/span&gt;&lt;span class="lnt"&gt;60
&lt;/span&gt;&lt;span class="lnt"&gt;61
&lt;/span&gt;&lt;span class="lnt"&gt;62
&lt;/span&gt;&lt;span class="lnt"&gt;63
&lt;/span&gt;&lt;span class="lnt"&gt;64
&lt;/span&gt;&lt;span class="lnt"&gt;65
&lt;/span&gt;&lt;span class="lnt"&gt;66
&lt;/span&gt;&lt;span class="lnt"&gt;67
&lt;/span&gt;&lt;span class="lnt"&gt;68
&lt;/span&gt;&lt;span class="lnt"&gt;69
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// SUBSYSTEM CLASSES&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;// File logging subsystem&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FileLogger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;logToFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Logging to file: &amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;// Console logging subsystem&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ConsoleLogger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;logToConsole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Logging to console: &amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;// Remote server logging subsystem&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RemoteServerLogger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;logToServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Sending log to remote server: &amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;// FACADE CLASS&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LoggerFacade&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FileLogger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fileLogger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ConsoleLogger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;consoleLogger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RemoteServerLogger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;remoteServerLogger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;LoggerFacade&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fileLogger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FileLogger&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;consoleLogger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ConsoleLogger&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;remoteServerLogger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RemoteServerLogger&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fileLogger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;logToFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;consoleLogger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;logToConsole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;remoteServerLogger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;logToServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Log message processed.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;// CLIENT CODE&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Main&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LoggerFacade&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LoggerFacade&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;This is a log message.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Unified logging&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;// CLIENT CODE WITHOUT FACADE&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Main&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Create instances of each logging subsystem&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FileLogger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fileLogger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FileLogger&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ConsoleLogger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;consoleLogger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ConsoleLogger&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RemoteServerLogger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;remoteServerLogger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RemoteServerLogger&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Log a message using all the logging mechanisms&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;This is a log message.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fileLogger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;logToFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;consoleLogger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;logToConsole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;remoteServerLogger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;logToServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Log message processed.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;As you can see from the example above, the client code without &lt;strong&gt;facade&lt;/strong&gt; is more complex even using just three classes, imagine what could happen with way more classes.&lt;/p&gt;
&lt;h2 id="applications"&gt;Applications
&lt;/h2&gt;&lt;p&gt;You can use &lt;strong&gt;facade&lt;/strong&gt; in such scenarios:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You need a limited subset of functionalities from a complex system&lt;/li&gt;
&lt;li&gt;You want to organize a subsystem into distinct logical layers&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Design Patterns: Decorator</title><link>https://bstz.it/p/design-patterns-decorator/</link><pubDate>Mon, 02 Sep 2024 00:00:00 +0000</pubDate><guid>https://bstz.it/p/design-patterns-decorator/</guid><description>&lt;p&gt;The Decorator is a &lt;strong&gt;structural design pattern&lt;/strong&gt; that allows you to add new functionalities to existing objects by wrapping them in new classes.&lt;/p&gt;
&lt;h2 id="problem"&gt;Problem
&lt;/h2&gt;&lt;p&gt;When working on a project, a scenario in which you need to add and combine different functionalities can happen.
The first approach to solve this problem could be creating a set of subclasses for each functionality, and this could work for a small set of classes.
But what if you need to add and combine a hundred different functionalities? The possible combination are really too much.
Furthermore, inheritance is static and changing behavior at runtime would be impossible.&lt;/p&gt;
&lt;h2 id="solution"&gt;Solution
&lt;/h2&gt;&lt;p&gt;That&amp;rsquo;s where the &lt;strong&gt;decorator pattern&lt;/strong&gt; comes into play.
It lets you wrap an object in a class that implements the same contract as the wrapped object, effectively &amp;ldquo;decorating&amp;rdquo; the object with new functionalities.
The key advantages of this approach include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Flexibility&lt;/strong&gt;: you can add new functionalities without modifying the existing code&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reusability&lt;/strong&gt;: decorators can be combined in different ways to create complex scenarios&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Single responsibility principle&lt;/strong&gt;: each decorator adds only the set of concerns it needs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://bstz.it/p/design-patterns-decorator/DecoratorDiagram.svg"
loading="lazy"
alt="DecoratorDiagram.svg"
&gt;&lt;/p&gt;
&lt;h2 id="real-world-example"&gt;Real-World example
&lt;/h2&gt;&lt;p&gt;In a typical software application, you might have a notification system that sends out alerts, updates, or messages to users. This system could start simple, just sending plain notifications. However, as the application grows, new requirements often emerge, such as:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Multiple Delivery Channels&lt;/strong&gt;: Users might want to receive notifications via different channels, such as email, SMS, or even push notifications.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Message Formatting&lt;/strong&gt;: Depending on the target channel or user preference, notifications might need to be formatted differently. For instance, JSON or XML formats might be needed for integrations, or plain text might suffice for others.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Logging and Monitoring&lt;/strong&gt;: To ensure that notifications are sent correctly and to troubleshoot issues, logging every notification becomes necessary.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security Features&lt;/strong&gt;: Adding encryption or signing of messages for security purposes might also become a requirement.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Customizable Behavior&lt;/strong&gt;: Different types of notifications (e.g., alerts, promotions, reminders) may need specific handling or extra processing.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Building all these features directly into a single notification class would make the class complex, hard to maintain, and less flexible. Moreover, using inheritance to handle these variations would result in an explosion of subclasses, each representing a combination of features, leading to a rigid and tangled class hierarchy.&lt;/p&gt;
&lt;h2 id="java-example"&gt;Java example
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;span class="lnt"&gt;43
&lt;/span&gt;&lt;span class="lnt"&gt;44
&lt;/span&gt;&lt;span class="lnt"&gt;45
&lt;/span&gt;&lt;span class="lnt"&gt;46
&lt;/span&gt;&lt;span class="lnt"&gt;47
&lt;/span&gt;&lt;span class="lnt"&gt;48
&lt;/span&gt;&lt;span class="lnt"&gt;49
&lt;/span&gt;&lt;span class="lnt"&gt;50
&lt;/span&gt;&lt;span class="lnt"&gt;51
&lt;/span&gt;&lt;span class="lnt"&gt;52
&lt;/span&gt;&lt;span class="lnt"&gt;53
&lt;/span&gt;&lt;span class="lnt"&gt;54
&lt;/span&gt;&lt;span class="lnt"&gt;55
&lt;/span&gt;&lt;span class="lnt"&gt;56
&lt;/span&gt;&lt;span class="lnt"&gt;57
&lt;/span&gt;&lt;span class="lnt"&gt;58
&lt;/span&gt;&lt;span class="lnt"&gt;59
&lt;/span&gt;&lt;span class="lnt"&gt;60
&lt;/span&gt;&lt;span class="lnt"&gt;61
&lt;/span&gt;&lt;span class="lnt"&gt;62
&lt;/span&gt;&lt;span class="lnt"&gt;63
&lt;/span&gt;&lt;span class="lnt"&gt;64
&lt;/span&gt;&lt;span class="lnt"&gt;65
&lt;/span&gt;&lt;span class="lnt"&gt;66
&lt;/span&gt;&lt;span class="lnt"&gt;67
&lt;/span&gt;&lt;span class="lnt"&gt;68
&lt;/span&gt;&lt;span class="lnt"&gt;69
&lt;/span&gt;&lt;span class="lnt"&gt;70
&lt;/span&gt;&lt;span class="lnt"&gt;71
&lt;/span&gt;&lt;span class="lnt"&gt;72
&lt;/span&gt;&lt;span class="lnt"&gt;73
&lt;/span&gt;&lt;span class="lnt"&gt;74
&lt;/span&gt;&lt;span class="lnt"&gt;75
&lt;/span&gt;&lt;span class="lnt"&gt;76
&lt;/span&gt;&lt;span class="lnt"&gt;77
&lt;/span&gt;&lt;span class="lnt"&gt;78
&lt;/span&gt;&lt;span class="lnt"&gt;79
&lt;/span&gt;&lt;span class="lnt"&gt;80
&lt;/span&gt;&lt;span class="lnt"&gt;81
&lt;/span&gt;&lt;span class="lnt"&gt;82
&lt;/span&gt;&lt;span class="lnt"&gt;83
&lt;/span&gt;&lt;span class="lnt"&gt;84
&lt;/span&gt;&lt;span class="lnt"&gt;85
&lt;/span&gt;&lt;span class="lnt"&gt;86
&lt;/span&gt;&lt;span class="lnt"&gt;87
&lt;/span&gt;&lt;span class="lnt"&gt;88
&lt;/span&gt;&lt;span class="lnt"&gt;89
&lt;/span&gt;&lt;span class="lnt"&gt;90
&lt;/span&gt;&lt;span class="lnt"&gt;91
&lt;/span&gt;&lt;span class="lnt"&gt;92
&lt;/span&gt;&lt;span class="lnt"&gt;93
&lt;/span&gt;&lt;span class="lnt"&gt;94
&lt;/span&gt;&lt;span class="lnt"&gt;95
&lt;/span&gt;&lt;span class="lnt"&gt;96
&lt;/span&gt;&lt;span class="lnt"&gt;97
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Component Interface&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;Notifier&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;// Concrete Component&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BasicNotifier&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;implements&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Notifier&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Sending basic notification: &amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;// Decorator Class&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kd"&gt;abstract&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NotifierDecorator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;implements&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Notifier&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;protected&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Notifier&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wrappedNotifier&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;NotifierDecorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Notifier&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;notifier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;wrappedNotifier&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;notifier&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wrappedNotifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;// Concrete Decorators&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EmailNotifier&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;extends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NotifierDecorator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;EmailNotifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Notifier&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;notifier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notifier&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Sending email notification: &amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SMSNotifier&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;extends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NotifierDecorator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;SMSNotifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Notifier&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;notifier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notifier&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Sending SMS notification: &amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LoggingNotifier&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;extends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NotifierDecorator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;LoggingNotifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Notifier&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;notifier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notifier&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Logging notification: &amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;JsonFormatNotifier&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;extends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NotifierDecorator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;JsonFormatNotifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Notifier&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;notifier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notifier&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jsonMessage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{ \&amp;#34;message\&amp;#34;: \&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;\&amp;#34; }&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jsonMessage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;// Client code&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NotificationSystem&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Notifier&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;notifier&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;BasicNotifier&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;notifier&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EmailNotifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notifier&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;notifier&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SMSNotifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notifier&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;notifier&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LoggingNotifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notifier&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;notifier&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JsonFormatNotifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notifier&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;notifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Hello, Decorator Pattern!&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="applications"&gt;Applications
&lt;/h2&gt;&lt;p&gt;Use the &lt;strong&gt;decorator pattern&lt;/strong&gt; when these cases occur:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Stream and I/O Operations&lt;/strong&gt;: Enhancing streams with buffering, encryption, or compression.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Logging Systems&lt;/strong&gt;: Adding logging functionality around method calls or operations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;File and Data Processing&lt;/strong&gt;: Applying filters, transformations, or validations to data streams.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Notification Systems&lt;/strong&gt;: Extending notifications with email, SMS, logging, and formatting features.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Serialization&lt;/strong&gt;: Converting objects to formats like JSON, XML, or CSV dynamically.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dynamic Feature Toggling&lt;/strong&gt;: Enabling or disabling features at runtime without modifying core code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Error Handling&lt;/strong&gt;: Wrapping operations with error handling, retries, or fallbacks.&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Design Patterns: Adapter</title><link>https://bstz.it/p/design-patterns-adapter/</link><pubDate>Fri, 30 Aug 2024 00:00:00 +0000</pubDate><guid>https://bstz.it/p/design-patterns-adapter/</guid><description>&lt;p&gt;Adapter is a &lt;strong&gt;structural design pattern&lt;/strong&gt; that enables one component to work with another that has an incompatible interface.&lt;/p&gt;
&lt;h2 id="problem"&gt;Problem
&lt;/h2&gt;&lt;p&gt;In software development, you might encounter situations where a component needs to collaborate with another that has a completely incompatible interface. Often, this incompatibility arises because the component is part of a third-party library or legacy code that cannot be modified.&lt;/p&gt;
&lt;h2 id="solution"&gt;Solution
&lt;/h2&gt;&lt;p&gt;This is where the Adapter pattern becomes useful. You can create an &lt;strong&gt;adapter&lt;/strong&gt; that converts the data or interface from one format to another, allowing the components to communicate effectively. For instance, you can build an adapter that converts XML data into JSON and passes it to a library that only understands JSON.&lt;/p&gt;
&lt;p&gt;In this scenario, the main actors are the adapter and the adaptee. They do not—and cannot—share a common interface.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://bstz.it/p/design-patterns-adapter/AdapterDiagram.svg"
loading="lazy"
alt="AdapterDiagram.svg"
&gt;&lt;/p&gt;
&lt;h2 id="real-world-example"&gt;Real-World example
&lt;/h2&gt;&lt;p&gt;Imagine you&amp;rsquo;re developing an application that needs to measure temperature. You have a remote sensor that provides temperature readings in Celsius, but your application uses a library that only works with Fahrenheit. Since you cannot modify the library to accept Celsius, you need a way to convert the temperature readings before passing them to the library.&lt;/p&gt;
&lt;p&gt;In this scenario, you can create an adapter that converts the Celsius readings from the sensor into Fahrenheit, allowing your application to work seamlessly with the existing library.&lt;/p&gt;
&lt;h2 id="java-example"&gt;Java example
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// adaptee&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CelsiusThermometer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getTemperature&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;25&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// returns temperature in Celsius&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;// adapter&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TemperatureAdapter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;implements&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FahrenheitThermometer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CelsiusThermometer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;celsiusThermometer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;TemperatureAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CelsiusThermometer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;celsiusThermometer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;celsiusThermometer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;celsiusThermometer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getTemperatureInFahrenheit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;celsius&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;celsiusThermometer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTemperature&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;celsius&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;9&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;32&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Convert to Fahrenheit&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;// client interface&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;FahrenheitThermometer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getTemperatureInFahrenheit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;// client code&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Main&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CelsiusThermometer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;celsiusThermometer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CelsiusThermometer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FahrenheitThermometer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fahrenheitThermometer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TemperatureAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;celsiusThermometer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Temperature in Fahrenheit: &amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fahrenheitThermometer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTemperatureInFahrenheit&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="applications"&gt;Applications
&lt;/h2&gt;&lt;p&gt;You can use the Adapter pattern in the following situations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;When you want to reuse legacy subclasses that lack certain functionality, and you cannot modify the superclass.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Extending these subclasses might lead to code duplication and violate clean code principles.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When you need to interact with a third-party library that functions differently from your codebase.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Design Patterns: Proxy</title><link>https://bstz.it/p/design-patterns-proxy/</link><pubDate>Thu, 29 Aug 2024 00:00:00 +0000</pubDate><guid>https://bstz.it/p/design-patterns-proxy/</guid><description>&lt;p&gt;Proxy is a &lt;strong&gt;structural pattern&lt;/strong&gt; that lets you put a placeholder between the clients and a real object.&lt;/p&gt;
&lt;h2 id="problem"&gt;Problem
&lt;/h2&gt;&lt;p&gt;Imagine an application where there&amp;rsquo;s a client that communicates with an object that&amp;rsquo;s very heavy in terms of resources. You may want to implement lazy loading but it could lead to code duplication.
Alternatively, the object might require pre-processing or post-processing.&lt;/p&gt;
&lt;h2 id="solution"&gt;Solution
&lt;/h2&gt;&lt;p&gt;This is where the &lt;strong&gt;proxy pattern&lt;/strong&gt; comes into play.
With this pattern, the client interacts with the proxy, which then communicates with the real object, potentially performing additional tasks before returning the result to the client.&lt;/p&gt;
&lt;p&gt;In this scenario, there are three key components: the client, the proxy, and the real object. Both the proxy and the real object should implement the same interface, ensuring that the client remains unaware of which object it is communicating with.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://bstz.it/p/design-patterns-proxy/ProxyDiagram.svg"
loading="lazy"
alt="ProxyDiagram.svg"
&gt;&lt;/p&gt;
&lt;h2 id="real-world-example"&gt;Real-World example
&lt;/h2&gt;&lt;p&gt;Imagine you have a remote service that fetches data from a server. Accessing this service might be slow or expensive, so you want to add some logging to see when it&amp;rsquo;s being accessed. Instead of modifying the service itself, you can use a proxy to handle the logging.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://bstz.it/p/design-patterns-proxy/RemoteServiceDiagram.svg"
loading="lazy"
alt="remoteServiceDiagram"
&gt;&lt;/p&gt;
&lt;h2 id="java-example"&gt;Java example
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;span class="lnt"&gt;43
&lt;/span&gt;&lt;span class="lnt"&gt;44
&lt;/span&gt;&lt;span class="lnt"&gt;45
&lt;/span&gt;&lt;span class="lnt"&gt;46
&lt;/span&gt;&lt;span class="lnt"&gt;47
&lt;/span&gt;&lt;span class="lnt"&gt;48
&lt;/span&gt;&lt;span class="lnt"&gt;49
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// The interface of a remote service&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;DataService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;// The concrete implementation of a real remote service&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RealDataService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;implements&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DataService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Simulate a delay as if fetching data from a remote server&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 2-second delay&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;InterruptedException&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;printStackTrace&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Data from remote server&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;// the concrete implementation of a remote service proxy, with added logging&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DataServiceProxy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;implements&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DataService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RealDataService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;realDataService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;DataServiceProxy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;realDataService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RealDataService&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Logging: Fetching data from remote server...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;realDataService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Logging: Data fetched successfully.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;// The client code&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Main&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DataService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dataService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DataServiceProxy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Fetch data using the proxy&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Client: Requesting data...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dataService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Client: Received data -&amp;gt; &amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="applications"&gt;Applications
&lt;/h2&gt;&lt;p&gt;The proxy pattern can be applied in various ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Remote Proxy&lt;/strong&gt;: When the real object is located remotely, and the proxy makes a remote call to fetch it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Virtual Proxy&lt;/strong&gt;: When you want to lazily load a resource-intensive object.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Protection Proxy&lt;/strong&gt;: When you need to perform security checks before interacting with or returning the real object.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Caching Proxy&lt;/strong&gt;: When you need to cache the real object to avoid fetching it repeatedly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Logging Proxy&lt;/strong&gt;: When you need to add logging before or after interacting with the real object.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="tips"&gt;Tips
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;It is not always possible to extract a common interface from a pre-existing class, especially if doing so requires altering all client code or if the class belongs to a third-party library. In the first case, consider making the proxy a subclass. In the latter case, consider using the &lt;a class="link" href="https://bstz.it/p/design-patterns-adapter/" target="_blank" rel="noopener"
&gt;adapter pattern&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Postman + Keycloak – How to automatically get the authentication token</title><link>https://bstz.it/p/postman--keycloak-how-to-automatically-get-the-authentication-token/</link><pubDate>Tue, 20 Aug 2024 00:00:00 +0000</pubDate><guid>https://bstz.it/p/postman--keycloak-how-to-automatically-get-the-authentication-token/</guid><description>&lt;p&gt;It often happens that you have to test REST services that require authentication, perhaps with keycloak. With Postman you can automatically get the access token before making each REST call. Let&amp;rsquo;s see how&lt;/p&gt;
&lt;h2 id="what-is-postman"&gt;What is Postman
&lt;/h2&gt;&lt;p&gt;If you develop backends you will surely know what Postman is, if not (or if you are new to the industry), allow me to introduce it to you:&lt;/p&gt;
&lt;p&gt;Postman is (to put it very simply) a platform for building, testing and cataloging APIs. It can be downloaded for free from the &lt;a class="link" href="https://www.postman.com/" title="official site"
target="_blank" rel="noopener"
&gt;official website&lt;/a&gt; and also has paid plans for teams and more complex projects, but the free version is fine for us.&lt;/p&gt;
&lt;p&gt;One of the most convenient features is the possibility of cataloging all our requests in what are called &lt;strong&gt;collections&lt;/strong&gt; , which are a concept very similar to directories.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://bstz.it/p/postman--keycloak-how-to-automatically-get-the-authentication-token/01.png"
width="762"
height="634"
srcset="https://bstz.it/p/postman--keycloak-how-to-automatically-get-the-authentication-token/01_hu_b86a7c919aeeb884.png 480w, https://bstz.it/p/postman--keycloak-how-to-automatically-get-the-authentication-token/01_hu_f198e7db531df276.png 1024w"
loading="lazy"
alt="Postman Collections"
class="gallery-image"
data-flex-grow="120"
data-flex-basis="288px"
&gt;
&lt;strong&gt;Each collection can be configured in its own way and this is exactly what we are going to do to automatically obtain the keycloak&lt;/strong&gt; authentication token .&lt;/p&gt;
&lt;h2 id="pre-request-script"&gt;Pre-request script
&lt;/h2&gt;&lt;p&gt;Pre-request scripts are, in fact, JavaScript scripts that are executed before a request. They can be inserted in two different places: within the single request or in the collection; the advantage of doing it in the collection is that in this way all requests within it will automatically execute the script.&lt;/p&gt;
&lt;p&gt;We then open our collection and in the &lt;strong&gt;Pre-request Script&lt;/strong&gt; tab we add the following code, obviously being careful to make the appropriate substitutions in the first part with the variables:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;client_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;your keycloak client_id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;client_secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;your keycloak secret&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;server_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;keycloak auth url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;realm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;keycloak realm&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;username&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;token_endpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;server_url&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/auth/realms/&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;realm&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/protocol/openid-connect/token&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;details&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;grant_type&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;username&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;formBody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;details&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;encodedKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;encodedValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;details&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;formBody&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;encodedKey&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;=&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;encodedValue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;formBody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;formBody&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;amp;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendRequest&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;token_endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;Content-Type&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;application/x-www-form-urlencoded&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;Authorization&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Basic &amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;btoa&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client_id&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;:&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;client_secret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;formBody&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jsonResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jsonResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;collectionVariables&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;access-token&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;jsonResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Then we open the &lt;strong&gt;Authorization&lt;/strong&gt; tab and set the type field as &lt;em&gt;Bearer Token&lt;/em&gt; and the Token field as&lt;code&gt;{{access-token}}&lt;/code&gt;
&lt;img src="https://bstz.it/p/postman--keycloak-how-to-automatically-get-the-authentication-token/02.png"
width="1416"
height="597"
srcset="https://bstz.it/p/postman--keycloak-how-to-automatically-get-the-authentication-token/02_hu_c661d0d7793fa50b.png 480w, https://bstz.it/p/postman--keycloak-how-to-automatically-get-the-authentication-token/02_hu_e30fe4465b870091.png 1024w"
loading="lazy"
alt="Postman Authorization tab"
class="gallery-image"
data-flex-grow="237"
data-flex-basis="569px"
&gt;&lt;/p&gt;
&lt;h2 id="single-request"&gt;Single request
&lt;/h2&gt;&lt;p&gt;At this point the last thing to do is to set the individual requests so that they inherit the authorization mode from the collection. To do this we open our request and in the Authorization tab we set the Type field as &lt;em&gt;Inherit auth from parent&lt;/em&gt; .&lt;/p&gt;
&lt;p&gt;&lt;img src="https://bstz.it/p/postman--keycloak-how-to-automatically-get-the-authentication-token/03.png"
width="790"
height="642"
srcset="https://bstz.it/p/postman--keycloak-how-to-automatically-get-the-authentication-token/03_hu_fad5ce3874492943.png 480w, https://bstz.it/p/postman--keycloak-how-to-automatically-get-the-authentication-token/03_hu_92755fdd60e891c3.png 1024w"
loading="lazy"
alt="Postman Request"
class="gallery-image"
data-flex-grow="123"
data-flex-basis="295px"
&gt;
At this point, every time we start the request, Postman will automatically contact the keycloak SSO to obtain the authentication token before making the request itself.&lt;/p&gt;
&lt;h2 id="conclusions"&gt;Conclusions
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;We have seen how to automatically obtain the keycloak&lt;/strong&gt; authentication token with &lt;strong&gt;Postman&lt;/strong&gt; . Did you already know this method? Do you know any other interesting uses of pre-request scripts?&lt;/p&gt;</description></item><item><title>Goodbye Wordpress, welcome Hugo!</title><link>https://bstz.it/p/goodbye-wordpress-welcome-hugo/</link><pubDate>Mon, 19 Aug 2024 00:00:00 +0000</pubDate><guid>https://bstz.it/p/goodbye-wordpress-welcome-hugo/</guid><description>&lt;p&gt;I&amp;rsquo;ve been looking for a quick and possibly &amp;ldquo;distraction-free&amp;rdquo; system to get back to writing on my blog for a while.&lt;/p&gt;
&lt;p&gt;WordPress had become slow and unmanageable, and with each update, it got worse and worse (probably my fault, but I neither had the time nor the desire to figure out the cause), so I thought: &amp;ldquo;What if I try &lt;a class="link" href="https://gohugo.io/" target="_blank" rel="noopener"
&gt;Hugo&lt;/a&gt;?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;After all, using WordPress, for what I do, is like using a &lt;em&gt;&lt;em&gt;insert expensive car brand here&lt;/em&gt;&lt;/em&gt; to go grocery shopping at a discount store: for instance, I don&amp;rsquo;t need comments on my posts, social media already provides that, and I don&amp;rsquo;t want to spend too much time managing security, drafts, cache, etc.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;EDIT: I added disqus comments, just in case&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A static site is perfect for what I need, and that&amp;rsquo;s where &lt;a class="link" href="https://gohugo.io/" target="_blank" rel="noopener"
&gt;Hugo&lt;/a&gt; comes in, which I hope will help me gain in:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Speed and performance&lt;/li&gt;
&lt;li&gt;Control over content&lt;/li&gt;
&lt;li&gt;Security&lt;/li&gt;
&lt;li&gt;Costs&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;I will definitely do another post in the future to summarize this choice.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="speed-and-performance"&gt;Speed and Performance
&lt;/h2&gt;&lt;p&gt;Even if you try to keep things minimal, with WordPress you still have to add plugins to manage certain aspects, like cache and SEO, and each plugin you add tends to slow down the platform a bit.&lt;/p&gt;
&lt;p&gt;Additionally, I&amp;rsquo;m not sure if it&amp;rsquo;s an issue with my hosting, Cloudflare, or who knows what, but lately I noticed that my WordPress blog was taking up to 20 seconds to load a simple page, despite having cache and Cloudflare&amp;rsquo;s CDN.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://gohugo.io/" target="_blank" rel="noopener"
&gt;Hugo&lt;/a&gt;, on the other hand, generates static content, which means no database, no processes running at page load, and no backend queries. You open the page, and it&amp;rsquo;s served to you as static content.&lt;/p&gt;
&lt;h2 id="control-over-content"&gt;Control over Content
&lt;/h2&gt;&lt;p&gt;Sure, WordPress is powerful and flexible, but after a while, I noticed I was spending more time managing WordPress itself than creating content, and that’s not good.&lt;/p&gt;
&lt;p&gt;With &lt;a class="link" href="https://gohugo.io/" target="_blank" rel="noopener"
&gt;Hugo&lt;/a&gt;, I just have to write markdown files to publish on GitHub Pages—nothing more, nothing less.&lt;/p&gt;
&lt;p&gt;By the way, I found a way to integrate &lt;a class="link" href="https://obsidian.md/" target="_blank" rel="noopener"
&gt;Obsidian&lt;/a&gt; with templates and daily notes, so I can automatically create a markdown template for the day&amp;rsquo;s post and start from there without having to copy and paste common parts from other files. It couldn’t be more convenient&amp;hellip;&lt;/p&gt;
&lt;h2 id="security-and-costs"&gt;Security and Costs
&lt;/h2&gt;&lt;p&gt;As mentioned earlier, I no longer need a backend or a database, which automatically improves security and reduces costs.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;In conclusion, switching from WordPress to &lt;a class="link" href="https://gohugo.io/" target="_blank" rel="noopener"
&gt;Hugo&lt;/a&gt; was a decision driven by my need for speed, control, security, and simplicity. WordPress remains an excellent platform for many, but for someone like me who seeks a lighter, more minimalist approach to blogging, &lt;a class="link" href="https://gohugo.io/" target="_blank" rel="noopener"
&gt;Hugo&lt;/a&gt; looks like a perfect solution.&lt;/p&gt;</description></item></channel></rss>