<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Muhammad Fahmi Rasyid</title>
    <description>The latest articles on DEV Community by Muhammad Fahmi Rasyid (@rasyidf).</description>
    <link>https://dev.to/rasyidf</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F670946%2Fe6b0ebc3-89b9-4745-b7fd-3b0fb997b863.jpeg</url>
      <title>DEV Community: Muhammad Fahmi Rasyid</title>
      <link>https://dev.to/rasyidf</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rasyidf"/>
    <language>en</language>
    <item>
      <title>Test Frontend Changes with Browser (Using Chromium Overrides)</title>
      <dc:creator>Muhammad Fahmi Rasyid</dc:creator>
      <pubDate>Tue, 09 Sep 2025 07:30:02 +0000</pubDate>
      <link>https://dev.to/rasyidf/test-frontend-changes-with-browser-using-chromium-overrides-41c</link>
      <guid>https://dev.to/rasyidf/test-frontend-changes-with-browser-using-chromium-overrides-41c</guid>
      <description>&lt;p&gt;I used to spend way too much time waiting for the backend team.&lt;br&gt;
If I needed to test what happens when an API returns something different - or worse, an error - I had two options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ask the backend team to change their response.&lt;/li&gt;
&lt;li&gt;Spin up a mock server and wire everything myself.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both were slow.&lt;/p&gt;

&lt;p&gt;Then I discovered something hidden in DevTools: &lt;strong&gt;Network Overrides&lt;/strong&gt;.&lt;br&gt;
It felt like flipping a secret switch. Suddenly I could control API responses without touching the backend. Here's how it went down.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Step 1: Opening the Toolbox&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I right clicked on the page → &lt;strong&gt;Inspect&lt;/strong&gt;.&lt;br&gt;
Instead of going to the Console, I switched over to the &lt;strong&gt;Sources&lt;/strong&gt; panel.&lt;/p&gt;

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

&lt;p&gt;On the left, there was a tab called &lt;strong&gt;Overrides&lt;/strong&gt; just sitting there quietly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Creating My Sandbox&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Clicking &lt;strong&gt;Overrides&lt;/strong&gt;, DevTools asked me to choose a folder.&lt;br&gt;
This folder is where my fake responses would live.&lt;/p&gt;

&lt;p&gt;I picked an empty folder, allowed Chrome to use it, and suddenly had my little override sandbox ready.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Grabbing a Response&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next, I went to the &lt;strong&gt;Network&lt;/strong&gt; tab, refreshed the page, and spotted the request I wanted: &lt;code&gt;/api/config&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Right-click → &lt;strong&gt;Save for overrides&lt;/strong&gt;.&lt;br&gt;
DevTools copied the real response into my local folder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Editing the Rules&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I opened the file under &lt;strong&gt;Sources &amp;gt; Overrides&lt;/strong&gt; and changed a few values.&lt;br&gt;
Something like turning &lt;code&gt;"featureEnabled": false&lt;/code&gt; into &lt;code&gt;"featureEnabled": true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5: Refresh &amp;amp; Smile&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I refreshed the page.&lt;br&gt;
The app now behaved as if the backend really returned my modified response.&lt;/p&gt;

&lt;p&gt;In the Network tab, a small icon appeared beside the request, showing it was overridden.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 6: Turning It Off&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When I was finished, I just unchecked &lt;strong&gt;Enable Local Overrides&lt;/strong&gt; in the Overrides panel.&lt;br&gt;
Everything went back to normal server responses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pro Tips: Mocking Errors &amp;amp; Timeouts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once I got comfortable, I started pushing things further:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mock an error response&lt;/strong&gt;:
Edit the override file and replace its body with an error JSON, e.g.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&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="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Internal Server Error"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in the headers, change &lt;code&gt;200 OK&lt;/code&gt; to &lt;code&gt;500 Internal Server Error&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simulate a timeout&lt;/strong&gt;:
In the &lt;strong&gt;Network&lt;/strong&gt; tab, right-click the request and select &lt;strong&gt;Block request URL&lt;/strong&gt;.
Now your app behaves as if the server never responded. Great for testing loaders and retries.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Closing Thoughts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Network Overrides turned my browser into a mini-mock server.&lt;br&gt;
No more waiting for backend changes, no more begging teammates for test data.&lt;br&gt;
Just fast, local experiments right inside DevTools.&lt;/p&gt;

&lt;p&gt;Next time you're stuck waiting on an API change, give Overrides a try - it might save you hours.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
    <item>
      <title>Building a 3D Formula Visualizer: Lessons Learned</title>
      <dc:creator>Muhammad Fahmi Rasyid</dc:creator>
      <pubDate>Mon, 10 Feb 2025 21:17:00 +0000</pubDate>
      <link>https://dev.to/rasyidf/building-a-3d-formula-visualizer-lessons-learned-1da8</link>
      <guid>https://dev.to/rasyidf/building-a-3d-formula-visualizer-lessons-learned-1da8</guid>
      <description>&lt;p&gt;Hey folks! 👋 A while ago, I embarked on a journey to build a 3D formula visualization tool using React, Three.js, and TypeScript. It sounded simple at first—just render some mathematical formulas in 3D and let users tweak parameters in real-time. But as with any project, the reality was far from straightforward. Along the way, I hit roadblocks, made mistakes, and learned a ton. If you're thinking of doing something similar, let me share what worked (and what didn’t) so you can hopefully avoid some of the pitfalls I encountered.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Lesson 1: Future-Proof Your Code
&lt;/h2&gt;

&lt;p&gt;When I first started, I knew I needed a way to manage different formulas cleanly. At first, I had a bunch of functions scattered everywhere, each handling its own calculations and geometry generation. This quickly became a nightmare. Every time I wanted to add a new formula, I had to copy-paste logic and tweak it manually—definitely not sustainable.&lt;/p&gt;

&lt;p&gt;Initially, I stored formulas in a simple object-based structure, something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formulas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Formula&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;gielis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Gielis&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A variation of the superformula&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;min&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="na"&gt;max&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="na"&gt;step&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="na"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;b&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;min&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="na"&gt;max&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="na"&gt;step&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="na"&gt;m&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;m&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;min&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="na"&gt;max&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;step&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="na"&gt;n1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;n1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;min&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="na"&gt;max&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="na"&gt;step&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="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;calculate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;phi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;n1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;n2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;n3&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cos&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;phi&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;n2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
        &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;phi&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;n3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;n1&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This worked at first, but as I added more formulas, maintaining this structure became unwieldy. So, I decided to go with an abstract base class to enforce structure. The idea was to have a consistent API that every formula would follow, making expansion easier and reducing redundancy. Here’s what I came up with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// filepath: /formulas/BaseFormula.ts&lt;/span&gt;
&lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BaseFormula&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;Formula&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FormulaMetadata&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="nf"&gt;calculate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FormulaParams&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="nf"&gt;createGeometry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FormulaParams&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;THREE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BufferGeometry&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To further improve maintainability, I transitioned to a class-based registry system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Formula&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../types/Formula&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;GielisFormula&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./GielisFormula&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;TerrainFormula&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./TerrainFormula&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SineInterferenceFormula&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./SineInterferenceFormula&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;GyroidFormula&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./GyroidFormula&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CellularNoiseFormula&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./CellularNoiseFormula&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MobiusFormula&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./MobiusFormula&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formulaRegistry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Formula&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;gielis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GielisFormula&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;terrainGen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TerrainFormula&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;sineInterference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SineInterferenceFormula&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;gyroid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GyroidFormula&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;cellularNoise&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CellularNoiseFormula&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;mobius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MobiusFormula&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getFormula&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Formula&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formula&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;formulaRegistry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;formula&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Formula type '&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;' not found`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;formula&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This simple refactor had a huge impact. Every formula now extended from this base class, keeping things modular and predictable. The calculations were separate from the Three.js geometry logic, making everything easier to debug and maintain. Plus, with TypeScript enforcing structure, I avoided a ton of runtime errors. It was a game-changer for scalability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson 2: Real-Time 3D Rendering is Tricky
&lt;/h2&gt;

&lt;p&gt;With the formula structure in place, I thought the hard part was over. But then came the real challenge—real-time rendering. I wanted users to tweak parameters and see their changes reflected instantly in the 3D visualization. Simple in theory, but in practice? Not so much.&lt;/p&gt;

&lt;p&gt;The first version of my app was sluggish. Every update caused unnecessary re-renders, and the scene management was all over the place. The solution? I had to rethink how updates were handled. I found two key improvements that made a world of difference.&lt;/p&gt;

&lt;p&gt;First, I centralized my scene management. Instead of each formula handling its own objects independently, I created a single place where objects were added, updated, and removed dynamically. This ensured that changes in parameters translated directly into visual updates without causing conflicts or memory leaks.&lt;/p&gt;

&lt;p&gt;Second, I made React state the driver of updates. Instead of manually modifying the Three.js scene, I let React’s state management handle the reactivity. Whenever a user adjusted a parameter, the state changed, triggering a recalculation and an efficient update to the scene. This cut down on unnecessary re-renders and made the visualization feel much smoother.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Next? 🚀
&lt;/h2&gt;

&lt;p&gt;Building this 3D formula visualizer was an eye-opening experience. While I solved many challenges, there’s still so much more to explore. In future iterations, I want to add support for custom formulas, letting users define their own equations and see them rendered in real-time. I also want to build a more intuitive parameter control system to make tweaking values feel seamless.&lt;/p&gt;

&lt;p&gt;You can check out the project on GitHub: &lt;a href="https://github.com/rasyidf/ultraformula" rel="noopener noreferrer"&gt;UltraFormula Repo&lt;/a&gt; and try the live demo here: &lt;a href="https://ultraformula.vercel.app/" rel="noopener noreferrer"&gt;UltraFormula on Vercel&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Right now, the app supports six different formula visualizations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Gielis Formula&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Perlin Noise Terrain Generator&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sine Interference Pattern&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gyroid Surface&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cellular Noise&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Möbius Strip&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Have you worked on something similar? What challenges did you face? I’d love to hear about your experiences—drop a comment below! 👇&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>threejs</category>
      <category>react</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Building a Board Game with React: Lessons Learned</title>
      <dc:creator>Muhammad Fahmi Rasyid</dc:creator>
      <pubDate>Mon, 20 Jan 2025 22:07:07 +0000</pubDate>
      <link>https://dev.to/rasyidf/building-a-board-game-with-react-lessons-learned-3ajh</link>
      <guid>https://dev.to/rasyidf/building-a-board-game-with-react-lessons-learned-3ajh</guid>
      <description>&lt;p&gt;Creating something from scratch is one of the best ways to learn—and that's precisely what I did when I decided to build &lt;strong&gt;Dakon Clash&lt;/strong&gt;, a digital board game. Inspired by an Indonesian traditional game using beads and fused with elements of Reversi, this project wasn’t just a fun way to engage with friends but also an ambitious playground for learning React Router v7 (aka Remix).&lt;/p&gt;

&lt;p&gt;Here’s a peek into my journey—the triumphs, mistakes, and lessons I learned along the way.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Why React and Remix?
&lt;/h2&gt;

&lt;p&gt;React has been my go-to framework at work, so sticking with it felt natural. When Remix (React Router v7) came out, I saw this as the perfect opportunity to kill two birds with one stone: learn Remix and build something I genuinely cared about. There’s no better way to learn than by diving into a project, especially when it’s challenging &lt;em&gt;and&lt;/em&gt; fun. Spoiler alert: it was both.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s New in Remix (React Router v7)?
&lt;/h2&gt;

&lt;p&gt;Remix merged into React Router, becoming React Router v7—a full-fledged framework. One standout feature that benefited my project was the routing. This allowed me to separate the board and controls into independent files, rendering them independently. The modular design improved maintainability and made the project easier to work with as it grew. It’s these little changes that made Remix such a joy to use.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrestling with State Management
&lt;/h2&gt;

&lt;p&gt;State management is the heart of any interactive application, but I underestimated just how tricky it could get. Here’s how my state management journey evolved:&lt;/p&gt;

&lt;h2&gt;
  
  
  Monolithic Chaos
&lt;/h2&gt;

&lt;p&gt;I started by throwing everything into zustand. I thought, "Hey, why not use a centralized store for everything?" At first, this approach worked, but as the game grew, so did the complexity. Debugging became a nightmare—it was harder to debug than to add features, which says a lot.&lt;/p&gt;

&lt;h2&gt;
  
  
  The All-in-One Engine
&lt;/h2&gt;

&lt;p&gt;Next, I refactored everything into an &lt;code&gt;EngineClass&lt;/code&gt;. This represented the entire game: board state, game logic, player interactions, everything. While it worked better than the previous version, the class became enormous and unwieldy. When I tried to set the state for other objects, recursion issues crept in, creating a tangled web of dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Breaking It Down
&lt;/h2&gt;

&lt;p&gt;Finally, I took a step back and broke the engine into smaller, more manageable pieces. I separated the logic into distinct classes: &lt;code&gt;Board&lt;/code&gt;, &lt;code&gt;Player&lt;/code&gt;, &lt;code&gt;GameMaster&lt;/code&gt;, and &lt;code&gt;Engine&lt;/code&gt;. The zustand store was now only responsible for holding the board state. This modular approach brought clarity and opened the door for future features like multiplayer, playback, and a bot engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Lesson:&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Don’t overcomplicate things. I made the mistake of focusing too much on details without seeing the bigger picture. Simplicity isn’t just elegant—it’s practical. Refactoring may feel like starting over, but it’s an essential part of the process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing the Game
&lt;/h2&gt;

&lt;p&gt;UI/UX design wasn’t something I’d done much of before, so I kept it simple. My brainstorming tool of choice? MS Paint. Yes, really.&lt;/p&gt;

&lt;p&gt;I drafted version 0 of the UI in Paint and used it as a rough blueprint. To refine this further, I used Vercel's v0.dev to improve the design and structure. But, Writing the actual code from these drafts was where the learning really happened. It wasn’t perfect, but it was mine, and the process was rewarding.&lt;/p&gt;

&lt;p&gt;Here's the first version&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Game Logic: Turning Ideas into Code
&lt;/h2&gt;

&lt;p&gt;Creating the game logic was my favorite part. I started by drafting pseudo-code for the game’s rules. The core idea revolves around placing beads into cells, triggering chain reactions when cells reach a certain threshold, and flipping opponent cells to your color. The challenge lay in converting these mechanics into code while keeping it efficient.&lt;/p&gt;

&lt;p&gt;One highlight was implementing the logic for analyzing the board and making weighted decisions. While it’s not perfect yet (and the AI is still a work in progress), I’m proud of how far it’s come. Converting a mental picture into working code was a satisfying challenge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Dive into a project to learn new tech.&lt;/strong&gt; Remix became much more approachable because I had a real project to build with it. Learning through doing always wins.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Think bigger but keep it simple.&lt;/strong&gt; I overcomplicated things early on, which led to headaches. Sometimes, the simplest solution is the best.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refactor without fear.&lt;/strong&gt; It’s easy to get attached to your first attempt, but don’t hesitate to rewrite if it’ll make your code better.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enjoy the process.&lt;/strong&gt; Even with blockers and setbacks, I had fun creating Dakon Clash. That’s what matters most.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  What’s Next?
&lt;/h2&gt;

&lt;p&gt;Dakon Clash is far from finished. My modular engine opens up possibilities for multiplayer, playback, and a smarter bot engine. There’s always room for improvement, but I’m happy with what I’ve learned and built so far.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building Dakon Clash wasn’t just about creating a game—it was about growing as a developer. If you’re thinking of tackling a side project, do it. You’ll learn, struggle, and grow. And who knows? You might end up with something you’re proud to share with the world.&lt;/p&gt;

&lt;p&gt;Feel free to share your thoughts, feedback, or similar experiences in the comments. And if you want to check out the code, I’ll leave a link below. Thanks for reading!&lt;/p&gt;

&lt;p&gt;The Game:&lt;br&gt;
&lt;a href="https://dakonclash.vercel.app/" rel="noopener noreferrer"&gt;DakonClash Game&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Code:&lt;br&gt;
&lt;a href="https://github.com/rasyidf/dakonclash" rel="noopener noreferrer"&gt;DakonClash on Github&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>gamedev</category>
      <category>learning</category>
      <category>opensource</category>
    </item>
    <item>
      <title>From Pods to Projects: Mastering Task Management the Whale-some Way</title>
      <dc:creator>Muhammad Fahmi Rasyid</dc:creator>
      <pubDate>Wed, 26 Jun 2024 20:08:24 +0000</pubDate>
      <link>https://dev.to/rasyidf/from-pods-to-projects-mastering-task-management-the-whale-some-way-5gad</link>
      <guid>https://dev.to/rasyidf/from-pods-to-projects-mastering-task-management-the-whale-some-way-5gad</guid>
      <description>&lt;p&gt;As I delved further into the project management process, I discovered an intriguing parallel: we can learn a lot from how whales manage their pods. Here's my journey into this oceanic analogy.&lt;/p&gt;

&lt;p&gt;In the vast ocean of project management, tracking progress can feel as daunting as navigating the deep blue sea. However, adopting the principles of a whale pod simplified the process for me, ensuring smooth and efficient project tracking. Allow me to take you through this journey, where the wisdom of whales provided a relaxed yet effective approach to project management.&lt;/p&gt;

&lt;h2&gt;
  
  
  Estimation Philosophy: Swimming in Sync
&lt;/h2&gt;

&lt;p&gt;When I started out, traditional project tracking seemed to demand pinpoint accuracy, like a lone whale searching for the perfect current. But watching whale pods taught me the power of consistency and collaboration. By maintaining steady estimates and adjusting predictions with a load factor—much like the gentle sway of a whale’s tail—teams can focus on completing tasks rather than obsessing over exact hours spent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Breaking Down Tasks: From Giant Whales to Agile Dolphins
&lt;/h2&gt;

&lt;p&gt;I noticed how a pod of whales collaborates to tackle large tasks, inspiring me to break down our project tasks into manageable pieces. We aimed for tasks that could be completed in 0.25 to 2 ideal days. It felt like teaching a whale calf to swim—small, achievable strokes leading to success. This approach made tracking easier and boosted team morale as we saw our progress ripple through the water.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simplified Progress Tracking: The Whale Song of Success
&lt;/h2&gt;

&lt;p&gt;Visual aids became our version of the hauntingly beautiful whale songs. A simple bar graph showing completed and estimated remaining tasks provided a clear overview. Rather than relying on complex predictions, we displayed our current progress and let stakeholders infer trends, much like interpreting the rhythms and patterns of a whale’s song.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling Scope Changes: Navigating New Currents
&lt;/h2&gt;

&lt;p&gt;Scope changes felt inevitable, much like the shifting ocean currents. We ensured our tracking tools could account for these changes to provide an accurate picture of progress. Visual representations of scope changes helped communicate their impact to stakeholders, just as a whale pod adapts to new waters and communicates through body language and song.&lt;/p&gt;

&lt;h2&gt;
  
  
  Encouraging User Engagement: The Pod Mentality
&lt;/h2&gt;

&lt;p&gt;Simplified tracking methods encouraged our team to engage actively, mirroring the close-knit cooperation of a whale pod. Visible progress, like a color-coded iteration board, provided immediate feedback and motivated team members. This approach allowed us to quickly identify the status of each task without delving into detailed reports, much like how whales recognize each other's roles and statuses within the pod.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sailing Smoothly with Simple Tools
&lt;/h2&gt;

&lt;p&gt;Effective project tracking doesn’t require complex tools or meticulous data recording. By focusing on consistent estimates, breaking down tasks, and using simple visual aids, project managers can maintain control and ensure steady progress. Inspired by the wisdom of whale pods, this approach offers a practical way to navigate the ocean of project management with grace and efficiency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In the end, project management is about finding harmony and balance within the team, much like a whale pod navigating the vast ocean together. By adopting these whale-inspired strategies, you can lead your team to smoother sailing and greater achievements. So, take a deep breath, dive in, and let the wisdom of the whales guide your project management journey.&lt;/p&gt;

&lt;p&gt;TL;DR:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I discovered how project management can be inspired by the behavior of whale pods. By adopting principles of consistency, collaboration, task breakdown, and simple visual tracking, teams can navigate projects more smoothly. Learn how to handle scope changes and boost team engagement, drawing wisdom from the gentle giants of the ocean.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>management</category>
      <category>projectmanagement</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
