<?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>Forem: MJ Dashtaki</title>
    <description>The latest articles on Forem by MJ Dashtaki (@mjd).</description>
    <link>https://forem.com/mjd</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%2F166235%2F54a66904-db4b-4b2a-b9d9-9c81a37e1354.png</url>
      <title>Forem: MJ Dashtaki</title>
      <link>https://forem.com/mjd</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/mjd"/>
    <language>en</language>
    <item>
      <title>How I Built a Personal Work Journal Dashboard with Claude</title>
      <dc:creator>MJ Dashtaki</dc:creator>
      <pubDate>Thu, 07 May 2026 10:49:25 +0000</pubDate>
      <link>https://forem.com/mjd/how-i-built-a-personal-work-journal-dashboard-with-claude-4nnk</link>
      <guid>https://forem.com/mjd/how-i-built-a-personal-work-journal-dashboard-with-claude-4nnk</guid>
      <description>&lt;p&gt;I've been keeping a work journal for almost 1.5 years now. Nothing fancy: just a Google Doc where I dump daily notes: what I shipped, what frustrated me, who I worked with, how I felt. After a year and a half, I had a wall of text that I never actually read back.&lt;/p&gt;

&lt;p&gt;Then I thought: what if Claude could read it &lt;em&gt;for&lt;/em&gt; me?&lt;/p&gt;




&lt;h2&gt;
  
  
  The idea: a Claude "skill"
&lt;/h2&gt;

&lt;p&gt;The inspiration came from a weekly ritual at my company: every Friday we have a Company Standup where each person shares the highlights and lowlights of their week. I always found myself scrambling to remember what I'd actually done, mentally skimming five days of work in thirty seconds while someone else was still talking.&lt;/p&gt;

&lt;p&gt;I thought: I already have all of this written down. I just need something to read it back to me, and ideally tell me more than I'd remember on my own.&lt;/p&gt;

&lt;p&gt;That's when I started building a Claude skill around my journal. I use Claude Code with a custom skills system (essentially markdown files that tell Claude how to handle specific commands). When I type &lt;code&gt;/journal&lt;/code&gt;, Claude reads a &lt;code&gt;SKILL.md&lt;/code&gt; file and knows exactly what to do: read my journal from Google Drive, analyze it, and render an interactive dashboard.&lt;/p&gt;

&lt;p&gt;The skill file looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;journal&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="s"&gt;Work journal dashboard skill. Reads your work journal from Google Drive,&lt;/span&gt;
  &lt;span class="s"&gt;a pasted text, or a local file, analyzes the entries, and renders an&lt;/span&gt;
  &lt;span class="s"&gt;interactive dashboard.&lt;/span&gt;

  &lt;span class="s"&gt;Trigger when user says:&lt;/span&gt;
  &lt;span class="s"&gt;- /journal&lt;/span&gt;
  &lt;span class="s"&gt;- "show my dashboard"&lt;/span&gt;
  &lt;span class="s"&gt;- "how was my week"&lt;/span&gt;
  &lt;span class="s"&gt;- "what did I ship this week"&lt;/span&gt;
  &lt;span class="s"&gt;- any mention of viewing or analyzing their work journal&lt;/span&gt;
&lt;span class="s"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The description isn't just documentation: it's what Claude uses to decide &lt;em&gt;when&lt;/em&gt; to activate this skill. Any phrasing works. As long as the intent is there, Claude picks it up.&lt;/p&gt;




&lt;h2&gt;
  
  
  How it works: three steps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Read the journal from Google Drive
&lt;/h3&gt;

&lt;p&gt;Claude has a Google Drive MCP (Model Context Protocol) connection. With one tool call, it fetches the full document:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Google Drive: read_file_content
fileId: 1Y8rsID6YF3Zhmnbt8t-...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My journal format is simple: bold date headings with bullet points underneath.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gs"&gt;**14.03**&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Got good feedback from Sarah in standup
&lt;span class="p"&gt;-&lt;/span&gt; Helped deploy the new search feature to prod
&lt;span class="p"&gt;-&lt;/span&gt; Started working on the notification redesign
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No schema, no special syntax. Just notes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Claude analyzes the text directly
&lt;/h3&gt;

&lt;p&gt;One important architectural note: &lt;strong&gt;the Anthropic API cannot be called from inside a browser widget&lt;/strong&gt; due to sandbox restrictions. Claude analyzes the journal in the conversation itself, not at render time.&lt;/p&gt;

&lt;p&gt;Instead, Claude itself analyzes the journal text in the conversation and produces a structured JSON object. This gets embedded directly into the widget as a &lt;code&gt;const D = {...}&lt;/code&gt;. No runtime API calls needed.&lt;/p&gt;

&lt;p&gt;The JSON schema covers everything I care about:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"weeks"&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="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"W1: 23–27 Mar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"week_highlights"&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="s2"&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;"week_lowlights"&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="s2"&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;"days"&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="nl"&gt;"date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"23.03"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"sentiment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"good"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"pos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"neg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&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="nl"&gt;"meetings_regular"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"meetings_friction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&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="nl"&gt;"appreciation"&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="nl"&gt;"from"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sarah"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Really clean PR structure, easy to review..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"14.03"&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="nl"&gt;"pain_points"&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="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Local dev / deploy process"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&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="nl"&gt;"people"&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="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sarah"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tags"&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="s2"&gt;"pairing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"reviews"&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="nl"&gt;"skills"&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="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Git worktree"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"13.04"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"skill"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The analysis rules I give Claude matter a lot here. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;appreciation&lt;/code&gt;: only include &lt;em&gt;explicitly&lt;/em&gt; positive feedback from others, no self-praise&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pain_points.count&lt;/code&gt;: count by how many distinct days mention that theme&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sentiment pos + neg&lt;/code&gt;: should sum to at most 90, leaving room for neutral days&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 3: Render an interactive dashboard
&lt;/h3&gt;

&lt;p&gt;Claude uses a visualizer tool to render an HTML widget inline in the chat. The widget has 9 tabs:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tab&lt;/th&gt;
&lt;th&gt;What it shows&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;This week&lt;/td&gt;
&lt;td&gt;Highlights and challenges for current + previous week&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Full month&lt;/td&gt;
&lt;td&gt;PR count, features shipped, overtime days&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sentiment&lt;/td&gt;
&lt;td&gt;Daily mood bars per week (green = positive, red = negative)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Appreciation&lt;/td&gt;
&lt;td&gt;Public feedback received from teammates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pain points&lt;/td&gt;
&lt;td&gt;Frequency bars for recurring frustrations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Meeting load&lt;/td&gt;
&lt;td&gt;Stacked bar chart: regular vs friction meetings per week&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Feature delivery&lt;/td&gt;
&lt;td&gt;Gantt-style bars from start to prod, colored by speed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Collaboration&lt;/td&gt;
&lt;td&gt;Who I worked with most, and how&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Skills picked up&lt;/td&gt;
&lt;td&gt;Tools and techniques learned, with date and context&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All styling uses CSS variables, so it adapts to light and dark mode automatically.&lt;/p&gt;

&lt;p&gt;Here's what it looks like:&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%2F4f4clmizd3j6jh5j1py7.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%2F4f4clmizd3j6jh5j1py7.png" alt="Full month tab showing PRs opened, features shipped, and month highlights" width="800" height="400"&gt;&lt;/a&gt;&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%2Fg0b0n4umc2nmrpklibjy.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%2Fg0b0n4umc2nmrpklibjy.png" alt="Pain points tab showing frequency bars and overtime log" width="800" height="359"&gt;&lt;/a&gt;&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%2Fezvaku7zy2bb55le41ya.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%2Fezvaku7zy2bb55le41ya.png" alt="Meetings tab showing meetings stats" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What I actually learned from it
&lt;/h2&gt;

&lt;p&gt;A year and a half of data, visualized, told me things I hadn't consciously registered:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The notification redesign took 18 days&lt;/strong&gt;, nearly 3x longer than the search feature (6 days). The journal shows why: repeated interruptions, unclear API ownership, and a PM pushing for delivery before the spec was settled.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My worst weeks weren't about the work&lt;/strong&gt;, they were about communication. Week 4 had the most "friction" meetings and the lowest sentiment scores, all tied to one recurring dynamic with a teammate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I picked up 11 new tools and skills in 6 weeks&lt;/strong&gt;, seeing it laid out as a card grid made me realize how much ground I'd covered during onboarding, even when it felt chaotic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Appreciation tab is underrated.&lt;/strong&gt; On a rough day, scrolling through actual quotes from teammates is genuinely useful.&lt;/p&gt;




&lt;h2&gt;
  
  
  The skill file: what makes it reusable
&lt;/h2&gt;

&lt;p&gt;The whole thing is driven by a single &lt;code&gt;SKILL.md&lt;/code&gt; file. When I want to update what the dashboard shows, I just edit the file. No code deployment, no config changes. The skill tells Claude:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When to trigger (any phrasing, any language)&lt;/li&gt;
&lt;li&gt;Where to read data from (Google Drive file ID)&lt;/li&gt;
&lt;li&gt;What JSON schema to produce&lt;/li&gt;
&lt;li&gt;What tabs to render and how&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is what makes it powerful: the "app" lives in a markdown file, and Claude is the runtime.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try it yourself
&lt;/h2&gt;

&lt;p&gt;The skill is open source. Install it in one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx skills add github.com/mjdashtaki/mj-skills
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or clone the repo directly: &lt;a href="https://github.com/mjdashtaki/mj-skills" rel="noopener noreferrer"&gt;github.com/mjdashtaki/mj-skills&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The skill supports three journal sources out of the box: Google Drive (via the Drive connector), direct paste, or a &lt;code&gt;.txt&lt;/code&gt;/&lt;code&gt;.md&lt;/code&gt; file upload. No hardcoded file IDs: Claude asks you on first run.&lt;/p&gt;

&lt;p&gt;If you want to build it yourself from scratch, the core idea works with any journal format:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Keep a simple daily log (bullet points are enough)&lt;/li&gt;
&lt;li&gt;Write a &lt;code&gt;SKILL.md&lt;/code&gt; that tells Claude when to activate and what to analyze&lt;/li&gt;
&lt;li&gt;Define the JSON schema for the data you care about&lt;/li&gt;
&lt;li&gt;Let Claude analyze and render. No backend needed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The hardest part isn't the code. It's deciding what questions you actually want to answer about your own work. Start there, and work backwards.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I write about frontend engineering, AI tooling, and developer experience. If you're building something similar, I'd love to hear about it.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>productivity</category>
      <category>showdev</category>
    </item>
    <item>
      <title>How I Built an AI Chat That Speaks As Me</title>
      <dc:creator>MJ Dashtaki</dc:creator>
      <pubDate>Sun, 22 Feb 2026 16:26:03 +0000</pubDate>
      <link>https://forem.com/mjd/how-i-built-an-ai-chat-that-speaks-as-me-1hn3</link>
      <guid>https://forem.com/mjd/how-i-built-an-ai-chat-that-speaks-as-me-1hn3</guid>
      <description>&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;p&gt;My portfolio site is a fairly standard one — it shows who I am, what I've built, and where I've worked. But it always felt passive. Someone lands on it, reads through the timeline, and leaves. No conversation, no depth beyond what fits on a page.&lt;/p&gt;

&lt;p&gt;I wanted to change that. What if a visitor could just &lt;em&gt;ask&lt;/em&gt;? "Has MJ worked with AI before?" "What was his biggest project?" "Does he have team leadership experience?" Instead of hunting through paragraphs, they'd get a direct, conversational answer, as if they were talking to me.&lt;/p&gt;

&lt;p&gt;That's the idea: an &lt;strong&gt;"Ask MJ"&lt;/strong&gt; chat widget, powered by AI, that answers in first person &lt;em&gt;as me&lt;/em&gt;, grounded in real content about my career.&lt;/p&gt;




&lt;h2&gt;
  
  
  The architecture: RAG in plain English
&lt;/h2&gt;

&lt;p&gt;I chose &lt;strong&gt;RAG&lt;/strong&gt;. It sounds complex but the concept is simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Store your knowledge&lt;/strong&gt; — take a document about yourself (in my case, a Markdown file with my full work history, skills, projects, and even performance review highlights and hobbies :) and break it into chunks. Embed each chunk as a vector(a mathematical representation of its meaning) and store it in a database.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;At query time, retrieve&lt;/strong&gt; — when someone asks a question, embed the question the same way, then find the chunks in the database whose vectors are closest in meaning. Those are the most relevant pieces of context.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Generate&lt;/strong&gt; — pass the retrieved context plus the user's question to a language model, and let it compose a reply.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result is a chat that only answers from &lt;em&gt;your&lt;/em&gt; content, not from the general internet. It can't hallucinate facts about you because it's constrained to what you've written.&lt;/p&gt;




&lt;h2&gt;
  
  
  Technology choices
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Supabase + pgvector — the memory
&lt;/h3&gt;

&lt;p&gt;I already use Supabase for other projects and it was a natural fit. What makes it perfect here is &lt;strong&gt;pgvector&lt;/strong&gt;, a Postgres extension that lets you store and query vector embeddings natively inside your database. No separate vector database service to set up, no extra infrastructure. One SQL migration, one similarity-search function, done.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="n"&gt;extension&lt;/span&gt; &lt;span class="n"&gt;if&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;exists&lt;/span&gt; &lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="n"&gt;bigserial&lt;/span&gt; &lt;span class="k"&gt;primary&lt;/span&gt; &lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1536&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The similarity search function ranks chunks by cosine distance, the closer two vectors are, the more semantically related they are. It's elegant and fast.&lt;/p&gt;

&lt;h3&gt;
  
  
  OpenAI — the embedder
&lt;/h3&gt;

&lt;p&gt;For turning text into vectors I used &lt;strong&gt;OpenAI's &lt;code&gt;text-embedding-3-small&lt;/code&gt;&lt;/strong&gt; model. It produces 1536-dimensional embeddings and is both affordable and high quality. Every chunk of my content gets embedded once (during the ingest step), and every user question gets embedded at query time before the similarity search.&lt;/p&gt;

&lt;h3&gt;
  
  
  Groq + Llama — the voice
&lt;/h3&gt;

&lt;p&gt;For the actual chat generation I chose &lt;strong&gt;Groq running Llama 3.3 70B&lt;/strong&gt;. Groq's inference hardware is genuinely fast. Responses start streaming back almost immediately, which is important for a good chat UX. Llama 3.3 70B is a capable open model that follows nuanced system prompts well.&lt;/p&gt;

&lt;p&gt;The system prompt was where I spent real attention. It's not enough to say "you are MJ." The model needs explicit rules: always speak in first person, never say "he" or "MJ has", you ARE this person, not an AI assistant describing someone else. That specificity is what keeps the voice consistent.&lt;/p&gt;

&lt;h3&gt;
  
  
  Next.js App Router — the glue
&lt;/h3&gt;

&lt;p&gt;The whole thing runs inside my existing &lt;strong&gt;Next.js&lt;/strong&gt; site with App Router. The chat API is a single route handler (&lt;code&gt;/api/chat&lt;/code&gt;) that handles the full pipeline, embed the query, retrieve from Supabase, build the prompt, stream Groq's response back to the browser as plain text. The client reads the stream token by token and appends to the UI in real time.&lt;/p&gt;

&lt;p&gt;No separate backend. No microservices. One route file.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building with Claude Code
&lt;/h2&gt;

&lt;p&gt;Here's what made this especially interesting: &lt;strong&gt;I built it using Claude Code&lt;/strong&gt; — Anthropic's AI coding agent that runs in the terminal alongside your editor.&lt;/p&gt;

&lt;p&gt;The workflow was collaborative in a way that felt genuinely different from using a regular code editor. I'd describe what I wanted, Claude would propose an implementation plan, we'd discuss the approach, and then it would write the code. When something broke, it diagnosed and fixed the issue in context, without losing track of what we were building.&lt;/p&gt;




&lt;h2&gt;
  
  
  What we built, step by step
&lt;/h2&gt;

&lt;p&gt;The full feature came together in these pieces:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Content file&lt;/strong&gt; (&lt;code&gt;mj-content.md&lt;/code&gt;) — a structured Markdown document with my full career history, AI experience, product stories, languages, and volunteering work. The source of truth for everything the chat knows.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ingest script&lt;/strong&gt; (&lt;code&gt;ingest.ts&lt;/code&gt;) — a one-time Node script that reads the content file, splits it on &lt;code&gt;---&lt;/code&gt; section boundaries into natural chunks, embeds each chunk via OpenAI, clears the old rows, and upserts everything into Supabase. Re-runnable whenever the content updates. One command: &lt;code&gt;yarn ingest&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;API route&lt;/strong&gt; (&lt;code&gt;/api/chat&lt;/code&gt;) — the server-side pipeline: embed the query → similarity search in Supabase → build a first-person system prompt with the retrieved context → stream Groq Llama's response back as a plain text &lt;code&gt;ReadableStream&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;ChatWidget component&lt;/strong&gt; — a &lt;code&gt;'use client'&lt;/code&gt; React component with a floating button, a slide-up panel, streaming message rendering, animated typing indicator, error handling, Escape-to-close, body scroll lock on mobile, and full-screen layout on small screens.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The result
&lt;/h2&gt;

&lt;p&gt;The chat works. Someone can visit &lt;a href="https://mjdashtaki.com" rel="noopener noreferrer"&gt;mjdashtaki.com&lt;/a&gt;, click "Ask about MJ from MJ's AI Twin", and ask something like "Has MJ led any teams?" or "What was his biggest technical challenge?" and get a warm, first-person answer grounded in real facts about my career.&lt;/p&gt;

&lt;p&gt;It's not a gimmick. It makes the portfolio interactive in a way a static page can't be. Recruiters, collaborators, or curious engineers can get real signal fast, without reading everything.&lt;/p&gt;

&lt;p&gt;The whole thing — from zero to deployed — was built in a single focused session, using a stack of four packages added to an existing Next.js project. That's what I find exciting about where tooling is right now: the distance from idea to working product has collapsed.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;A few things I'd like to add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Suggested questions&lt;/strong&gt; — surface a few starter prompts ("What's MJ's strongest skill?", "Is he available for freelance?") to help visitors who don't know where to begin.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But for a v1, I'm happy with it. It speaks as me, it knows my story, and it loads fast.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you want to build something similar on your own portfolio, the stack is: Supabase pgvector + OpenAI embeddings + Groq Llama + Next.js API route. The ingest script and route handler are each under 60 lines.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>supabase</category>
    </item>
    <item>
      <title>🚀 Accelerate Your Onboarding: How to Hit the Ground Running as a New Product Engineer</title>
      <dc:creator>MJ Dashtaki</dc:creator>
      <pubDate>Sun, 13 Apr 2025 16:21:47 +0000</pubDate>
      <link>https://forem.com/mjd/accelerate-your-onboarding-how-to-hit-the-ground-running-as-a-new-engineer-bf</link>
      <guid>https://forem.com/mjd/accelerate-your-onboarding-how-to-hit-the-ground-running-as-a-new-engineer-bf</guid>
      <description>&lt;p&gt;Joining a new company can feel like being dropped into the middle of a busy highway. There are new faces, unfamiliar tools, and processes that are still a mystery. But the faster you ramp up, the sooner you can start making an impact. Here’s a guide to help you onboard like a pro, especially if you’re a product or frontend engineer.&lt;/p&gt;

&lt;p&gt;When onboarding begins, you’ll likely have many meetings and tasks to manage. I just wanted to share how I approach my onboarding process to make it as fast and efficient as possible.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔍 1. Start With Curiosity: Ask for Documentation
&lt;/h2&gt;

&lt;p&gt;Also, all companies have a sort of documentation that can helps, so look for key resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Engineering handbook&lt;/li&gt;
&lt;li&gt;  Architecture overviews&lt;/li&gt;
&lt;li&gt;  Incident response guides&lt;/li&gt;
&lt;li&gt;  Release checklists&lt;/li&gt;
&lt;li&gt;  Feature lifecycle docs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before you dive into the code, ask your team:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;&lt;em&gt;Do we have documentation on engineering workflows, release processes, or incident handling?&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The best time to ask questions is during a meeting with your onboarding's meetings. If you need help from someone you don’t have a meeting with, &lt;strong&gt;take that as an opportunity to reach out and schedule a 1:1 meeting&lt;/strong&gt;, introduce yourself, and have a quick chat with someone who knows the product well.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Tip: If documentation is outdated or missing, take notes and help improve it. It’ll help the next joiner (and solidify your knowledge).&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🧠 2. Learn the Product, Not Just the Code
&lt;/h2&gt;

&lt;p&gt;Understanding the product and customer pain points makes you a better  product engineer. Don’t just look at functions, look at the &lt;strong&gt;&lt;em&gt;why&lt;/em&gt;&lt;/strong&gt; behind them. Some companies have walkthrough session, and they show you what is the product and how it works. This meeting ideally led by someone from PS or Sales who knows the platform in more detail. I really believe that having a solid understanding of the product and the business context helps us write better code. Ideally, this kind of session should happen in the first week of onboarding.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Regardless of whether we’re backend or frontend engineers, at the end of the day, we’re product engineers, and being able to see/feel the customer’s pain points puts us in a much stronger position to build meaningful solutions. &lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🛠️ 3. Understand the Codebase With Context
&lt;/h2&gt;

&lt;p&gt;Don’t just randomly browse files and start with a map. Before diving into the code, familiarize yourself with the structure and flow. Understanding these will help you navigate the codebase effectively and contribute to the project faster:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Look for &lt;strong&gt;README files&lt;/strong&gt; in each directory.&lt;/li&gt;
&lt;li&gt;  Ask for an &lt;strong&gt;architectural overview&lt;/strong&gt; or a recorded engineering walkthrough.&lt;/li&gt;
&lt;li&gt;  Find out where feature flags, state management, and routing are handled.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Ask:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  "What’s the entry point of the app?"&lt;/li&gt;
&lt;li&gt;  "How are new features usually added?"&lt;/li&gt;
&lt;li&gt;  "Is there a standard folder structure or naming convention we follow? Any documentation for it?"&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;🤝 Ask Designers: “What are our core user journeys?”&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🔄 4. Shadow the Process: Releases, Bugs &amp;amp; Incidents
&lt;/h2&gt;

&lt;p&gt;Not everything lives in a doc — real understanding often comes from observation. By shadowing key workflows like releases or incident handling, you'll get an inside look at how the team reacts under pressure, makes decisions, and collaborates. This hands-on exposure is invaluable for leveling up fast. I would say it’s tribal knowledge. Learn it by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Shadowing a release or pairing on a hotfix&lt;/li&gt;
&lt;li&gt;  Asking what happens when a P1 bug comes in&lt;/li&gt;
&lt;li&gt;  Attending a postmortem or retro session&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Tip: Join relevant Slack channels like &lt;code&gt;#&amp;lt;releases&amp;gt;&lt;/code&gt;, &lt;code&gt;#&amp;lt;bugs&amp;gt;&lt;/code&gt;, or &lt;code&gt;#&amp;lt;incidents&amp;gt;&lt;/code&gt;. Just observing can teach you a lot.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  ⚙️ 4. Learn the Development Process
&lt;/h2&gt;

&lt;p&gt;To become an effective contributor, it's crucial to understand how the team ships software. From how branches are managed to how features are released and bugs are tracked, learning these workflows early will help you navigate the codebase and collaborate efficiently with your team.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understand the branching strategy (feature branches, etc.).&lt;/li&gt;
&lt;li&gt;Learn the release cycle (weekly, bi-weekly, feature flags?).&lt;/li&gt;
&lt;li&gt;Check how code reviews are done — are there PR templates, guidelines?&lt;/li&gt;
&lt;li&gt;Figure out how bugs, tasks, and features are tracked (Jira, Linear, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📅 Ask: “What’s our process from task creation to deployment?”&lt;/p&gt;




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

&lt;p&gt;Onboarding is a two-way street: you’re learning, but &lt;strong&gt;you can also provide value quickly&lt;/strong&gt; by asking good questions, spotting gaps, and documenting what you learn. If you're intentional about it, you can go from new hire to high-impact team member in record time.&lt;/p&gt;

&lt;p&gt;Let curiosity lead the way — and &lt;strong&gt;don’t be afraid to ask&lt;/strong&gt;. The faster you understand how the business, product, and code fit together, the faster you'll build things that matter. Happy onboarding!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Quality Quest: The Importance of Reviewing Merge Request</title>
      <dc:creator>MJ Dashtaki</dc:creator>
      <pubDate>Tue, 25 Apr 2023 22:22:52 +0000</pubDate>
      <link>https://forem.com/mjd/quality-quest-the-importance-of-reviewing-merge-request-2kmb</link>
      <guid>https://forem.com/mjd/quality-quest-the-importance-of-reviewing-merge-request-2kmb</guid>
      <description>&lt;p&gt;I recall having a team lead when I was at my first job, and my revision control was showing the changes with red font color (I think it was Hg Tortoise 😄 ), and at some point, he told me, &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You should see the red lines of the code that you changed as blood; they should be taken seriously. Do not commit your changes till you make sure".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I still remember those words vividly, and they have stayed with me ever since. Quality is essential when it comes to software development, and it should never be taken lightly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why reviewing merge requests carefully is important
&lt;/h3&gt;

&lt;p&gt;Reviewing merge requests is a critical step in ensuring that the codebase remains high-quality, consistent, and reliable. Without proper review, code changes could introduce bugs (even by fixing a bug) or other issues that could lead to crashes, security vulnerabilities, or other problems. Reviewing merge requests helps catch these issues early on before they become bigger problems down the road.&lt;/p&gt;

&lt;h3&gt;
  
  
  It is not enough to only check the code quality
&lt;/h3&gt;

&lt;p&gt;When it comes to reviewing merge requests, it is crucial to take a comprehensive approach. One purpose of reviewing is to make sure the changes are consistent with the overall codebase's architecture and style however It is not enough to only check the code quality; there are several other aspects to consider, such as functionality, performance, security, documentation, and testing. Check out the feature/fix/hotfix/... branch and try to test it in action and ensure it's working as expected based on what's described in the ticket. Ensure that the changes are consistent with the overall codebase's architecture and style.&lt;/p&gt;

&lt;h3&gt;
  
  
  The MR checklist is a blast
&lt;/h3&gt;

&lt;p&gt;Create a checklist of all the aspects that need to be considered when reviewing a merge request. This checklist will help ensure that every aspect that we are concerned about is tested by the reviewer, even technical details.&lt;br&gt;
As an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* [ ] By adding this feature the payment information show still work
* [ ] It MUST be reviewed by Zsolt
* [ ] Preview mode should be handled in this case
- Technical writer review items:
    - [ ] Test coverage should be still more than 90%
    - [ ] Cache invalidation should be fine
    - [ ] Ensure a release milestone is set
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Different View, Different Perspective
&lt;/h3&gt;

&lt;p&gt;Ask for review from someone outside of your team/squad for some cases that you think [s]he has some insight in regard to the MR, as they can provide a fresh perspective and help identify issues that the team may have missed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mandatory reviewers
&lt;/h3&gt;

&lt;p&gt;Make it mandatory that the merge request must be approved by someone who is working with you on the same feature, who might have some ideas around your approach, or who has a reputation for maintaining high-quality code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Merge request template
&lt;/h3&gt;

&lt;p&gt;Actually, they come in handy in order to explain what the reason was for creating this merge request. Basically, it leads you to guidelines, instructions, and a checklist that developers can follow to ensure that their merge requests are of high quality, and they should provide all of the necessary information upfront.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;In conclusion, quality is a crucial aspect of software development, and it should never be taken lightly. Reviewing merge requests and using testing tools can help improve code quality, test coverage and identify potential issues before they become problems. By following best practices and continually striving to improve quality, we can create more stable and reliable software systems.&lt;/p&gt;

</description>
      <category>codequality</category>
      <category>mr</category>
      <category>pr</category>
    </item>
    <item>
      <title>Unknown, any and never types in Typescript</title>
      <dc:creator>MJ Dashtaki</dc:creator>
      <pubDate>Tue, 28 Mar 2023 19:18:02 +0000</pubDate>
      <link>https://forem.com/mjd/unknown-any-and-never-in-typescript-2o0h</link>
      <guid>https://forem.com/mjd/unknown-any-and-never-in-typescript-2o0h</guid>
      <description>&lt;h3&gt;
  
  
  Intro
&lt;/h3&gt;

&lt;p&gt;TypeScript is an open-source programming language that adds optional static typing to JavaScript. It is widely used for developing large-scale applications and has several types that help developers to write better code. In this article, we will discuss the differences between the &lt;code&gt;unknown&lt;/code&gt;, &lt;code&gt;any&lt;/code&gt;, and &lt;code&gt;never&lt;/code&gt; types in TypeScript.&lt;/p&gt;

&lt;h3&gt;
  
  
  Difference
&lt;/h3&gt;

&lt;p&gt;Let's take a look at a simple code example to understand the difference between &lt;code&gt;unknown&lt;/code&gt; and &lt;code&gt;any&lt;/code&gt; types:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello, world!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// No error at compile-time, but can throw runtime error&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the &lt;code&gt;getValue&lt;/code&gt; function returns a value of type &lt;code&gt;unknown&lt;/code&gt;, which means that the type of the value is not yet known during the execution of the program. When we assign the value to a variable value of type &lt;code&gt;any&lt;/code&gt;, we tell TypeScript to allow any type of value to be assigned to that variable, including the value returned by &lt;code&gt;getValue&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, when we call the &lt;code&gt;toUpperCase&lt;/code&gt; method on the value variable, TypeScript does not throw an error at compile-time, but it can still throw a runtime error if the value is not a string. This is because the any type does not provide any type safety, and the developer needs to ensure that the value is of the correct type before using it.&lt;/p&gt;

&lt;p&gt;On the other hand, if we change the type of the value variable to &lt;code&gt;unknown&lt;/code&gt;, TypeScript will require us to perform a type check before using the value, 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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello, world!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getValue&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="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// No error&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we perform a type check to ensure that the value variable is a string before calling the &lt;code&gt;toUpperCase&lt;/code&gt; method. This provides better type safety and prevents runtime errors.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;never&lt;/code&gt; type is different from &lt;code&gt;unknown&lt;/code&gt; and &lt;code&gt;any&lt;/code&gt; types, as it represents values that will never occur. Here's an example:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;throwError&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;never&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Something went wrong!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;throwError&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// No error&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the throwError function always throws an error, so it will never return a value. We can use the &lt;code&gt;never&lt;/code&gt; type as the return type of the function to communicate this to the compiler. This helps in improving the overall code quality and maintainability, as it clearly communicates the intent of the function.&lt;/p&gt;

&lt;h3&gt;
  
  
  Usecase:
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;unknown&lt;/code&gt; type in TypeScript should be used when the type of a value is not known at the time of declaration. It is particularly useful when working with dynamic data, such as data from API responses, as it provides better type safety and prevents runtime errors.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;any&lt;/code&gt; type should only be used as a last resort, when it is not possible to determine the type of a value. It should be avoided as much as possible, as it can lead to runtime errors and make the codebase difficult to maintain.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;never&lt;/code&gt; type should be used as a return type for functions that will never return or throw an error. This helps in making the code more readable and maintainable, as it clearly communicates the intent of the function.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion:
&lt;/h3&gt;

&lt;p&gt;In conclusion, TypeScript has several types that help developers to write better code. The &lt;code&gt;unknown&lt;/code&gt; type provides better type safety and is useful when working with dynamic data. The &lt;code&gt;any&lt;/code&gt; type should be avoided as much as possible, as it can lead to runtime errors and make the codebase difficult&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>any</category>
      <category>unknown</category>
      <category>never</category>
    </item>
    <item>
      <title>Methods of solving a problem!</title>
      <dc:creator>MJ Dashtaki</dc:creator>
      <pubDate>Mon, 06 Feb 2023 17:42:57 +0000</pubDate>
      <link>https://forem.com/mjd/methods-of-solving-a-problem-1go</link>
      <guid>https://forem.com/mjd/methods-of-solving-a-problem-1go</guid>
      <description>&lt;p&gt;It is very important for programmers/developers to be able to find a quick and at the same time optimal and good solutions for a problem/issue, which can be improved by experiencing and seeing programming solutions or best practices. However, there are a series of methods that generally help the programmer to find a quick and optimal solution to any problem, as I said. Basically, the first step to finding the best solution you have to understand and comprehend the problem well, It became a bit philosophical :) You should know comprehending and understanding are different from each other.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When you understand the problem you will find several solutions however when you comprehend the problem you will choose the best solution for it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To solve a problem, you have to understand and comprehend the problem well. Perhaps the example is that if you understand the problem well, you may have several solutions to it, and when you comprehend it well, you can choose the best solution.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Now, How do we know that we understood it well?!?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The solution that I highly recommend is that you use the &lt;a href="https://en.wikipedia.org/wiki/Rubber_duck_debugging" rel="noopener noreferrer"&gt;Rubber Ducking&lt;/a&gt; technique, that is, in order for you to realize that you understand the problem well or not. Let’s explain the problem to your colleague if he understands your problem, it means you understand the problem well and correctly so far. Otherwise, it means that you still need to understand the problem well. In fact, it may be an idea that this step may be very time consuming however it is very important because if you understand and comprehend the problem you can solve it better and faster.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to understand and comprehend?
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Well, now what do we do to understand the issue well? Somebody like Valinda Chan here recommends &lt;strong&gt;reading the issue at least three times&lt;/strong&gt;, here the word "at least" is very important.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Mr. John Sonmez who is the author of the book Soft Skills Here said he believes that if you want to get the best answer you should &lt;strong&gt;spend 70% of your time on the stage of understanding the problem&lt;/strong&gt;. It brings you the importance of this.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The next thing to do is &lt;strong&gt;solve the problem with three test data&lt;/strong&gt;. In fact, you are trying to understand the behavior of your problem with this data, also it will make you pay more attention to Edge Conditions and Corner Conditions.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Finally
&lt;/h3&gt;

&lt;p&gt;You should write your steps that you have solved the problem in the form of pseudo-code, and since you are writing in the steps in pseudo-code, it is much easier to understand at first glance, and besides, you are more involved in logic than code and for instance, you do not think about What should I use for looping now. In the next step, you should convert it to real code and the last step is optimizing and refactoring the code that you have written to get to the minimum code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Until you don't know what the problem is exactly, you should not jump over it. It is like an algorithm to solve a problem, So you have to move further step by step.&lt;/p&gt;

</description>
      <category>solvingproblem</category>
    </item>
    <item>
      <title>Smart use of “if/else”</title>
      <dc:creator>MJ Dashtaki</dc:creator>
      <pubDate>Mon, 06 Feb 2023 17:38:49 +0000</pubDate>
      <link>https://forem.com/mjd/smart-use-of-ifelse-1gml</link>
      <guid>https://forem.com/mjd/smart-use-of-ifelse-1gml</guid>
      <description>&lt;p&gt;Actually, this article is not against the &lt;code&gt;if/else&lt;/code&gt; but tries to show how following an anti-if pattern will improve your code quality. First thing first, I think we have to know how to use &lt;code&gt;if/else&lt;/code&gt; could be improved your code quality in terms of reading(readability) and hard to debug(maintainability)&lt;br&gt;
There are pretty many articles on the internet that lead you to prevent using &lt;code&gt;if/else&lt;/code&gt; and &lt;a href="https://francescocirillo.com/products/the-anti-if-campaign" rel="noopener noreferrer"&gt;being an Anti-If developer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In some cases using shorthand &lt;code&gt;if/else&lt;/code&gt; might also be helpful because it avoids branching your code which costs more in terms of complexity.&lt;br&gt;
For instance in this case you can get rid of &lt;code&gt;else&lt;/code&gt; just by &lt;code&gt;return&lt;/code&gt; there is no code branching too:&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%2Fg6s1ytj5mxh8cbds4f4f.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%2Fg6s1ytj5mxh8cbds4f4f.png" alt="No code branching" width="800" height="740"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Ternary Operator (?:)
&lt;/h3&gt;

&lt;p&gt;If you want to shorten If/else and in this case &lt;code&gt;else&lt;/code&gt; part is mandatory, you can use &lt;code&gt;?:&lt;/code&gt; operator as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;condition ? action-on-true : action-on-false(else)
// For instance
const gender = isMale ? 'Male' : 'Female';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I see this kinda short hand format used for null/undefined checking like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const avatarName = user.firstname ? user.firstname : user.lastname;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However in this case you can simply use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const avatarName = user.firstname ?? user.lastname
// or depends on your case you can use  "||"
const avatarName = user.firstname || user.lastname
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Logical Operator (&amp;amp;&amp;amp;)
&lt;/h3&gt;

&lt;p&gt;In another case, if you have only &lt;code&gt;if&lt;/code&gt; condition you can use &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; operator as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;condition &amp;amp;&amp;amp; action;
// For instance
!this.settings &amp;amp;&amp;amp; (this.settings = new TableSettings());
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also use polymorphism to avoid using &lt;code&gt;if/else&lt;/code&gt; like as an example below which is really more readable/maintainable, Here is the code :&lt;br&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%2Fcckoap29w10bx0tdmget.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%2Fcckoap29w10bx0tdmget.png" alt="no polymorphism" width="800" height="493"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And then it can be implemented by polymorphism:&lt;br&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%2Frnz9cezi3biebfl2nm3d.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%2Frnz9cezi3biebfl2nm3d.png" alt="polymorphism" width="800" height="678"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conslusion:
&lt;/h3&gt;

&lt;p&gt;I hope it did help but anyway in order to write a code that is reliable we have to apply best practices.&lt;/p&gt;

</description>
      <category>cleancode</category>
    </item>
  </channel>
</rss>
