<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Ravgeet Dhillon's Blog</title>
        <link>https://www.ravgeet.in</link>
        <description>Full Stack Developer and Technical Content Writer - sharing insights on web development, programming, and technology.</description>
        <lastBuildDate>Thu, 09 Apr 2026 02:06:51 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>Nuxt.js Feed Module</generator>
        <language>en</language>
        <ttl>60</ttl>
        <category>Web Development</category>
        <category>Programming</category>
        <category>Technology</category>
        <category>AI</category>
        <atom:link href="https://www.ravgeet.in/feed.xml" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Building a Real-Time Power Outage Monitor with ESP32 and Slack]]></title>
            <link>https://www.ravgeet.in/blog/building-a-real-time-power-outage-monitor-with-esp32-and-slack</link>
            <guid>https://www.ravgeet.in/blog/building-a-real-time-power-outage-monitor-with-esp32-and-slack</guid>
            <pubDate>Tue, 17 Mar 2026 05:48:17 GMT</pubDate>
            <description><![CDATA[The moment of realization happened in Singapore. I was thousands of miles away from home, enjoying a trip, when I went to check my home CCTV footage through my phone. The screen stayed black. "Connect]]></description>
            <content:encoded><![CDATA[The moment of realization happened in Singapore. I was thousands of miles away from home, enjoying a trip, when I went to check my home CCTV footage through my phone. The screen stayed black. "Connection Failed."

The internal monologue of a developer immediately goes to the worst-case scenario: *Did the router die? Is there a break-in? Did the server crash?*

The reality was much simpler, yet equally frustrating: a power cut. The cameras ran on their internal batteries until they hit 0%, leaving me in a complete information blackout. I didn't know if the power was out for ten minutes or ten hours.

I promised myself I wouldn't leave for another trip without a "Heartbeat" from my home in Amritsar.

### The Solution

I needed a non-invasive system (no cutting 220V mains wires), resilient to inverter switchover gaps, and capable of sending instant notifications.

I chose the **ESP32** for its built-in Wi-Fi and low power consumption. By pairing it with a Slack Webhook, I created a device that "shouts" the second the grid fails.

### The Hardware Stack

To keep things modular and "plug-and-play," I went with:

*   **ESP32 DevKit V1:** The brain of the operation.
    
*   **USB-TTL Converter:** This acts as the sensor. It brings the 5V USB signal down to a safe 3.3V for the ESP32 to read.
    
*   **1000µF Capacitor:** Essential for bridging the 20ms gap during inverter switchover. This prevents the ESP32 from rebooting during the transition.
    
*   Female-to-Female jumper wires
    
*   Some USB data cables
    

### Engineering the "Flicker Filter"

One of the biggest hurdles was electrical noise. GPIO 34 on the ESP32 is an input-only pin and can act like an antenna. Without a solid ground reference, the signal "flickered" between ON and OFF.

I solved this with a two-pronged approach:

1.  **Grounding Geometry:** Moving the ground wires to the same side of the board to stabilize the reference voltage.
    
2.  **Software Debounce:** I implemented a 3-second delay in the code. The system verifies that the power is *actually* out before sending a Slack alert, preventing false alarms from minor grid fluctuations.
    

### Watch the Journey

I documented the entire process—from navigating the local electronics markets in my city to the final "Production" test where I manually tripped the MCB.

*   **Watch the Documentary:** [YouTube - Making my first Electronics project](https://youtu.be/WmGZCAdeHMY)
    
*   **Get the Code:** [GitHub - esp32-power-alert](https://github.com/ravgeetdhillon/esp32-power-alert)
    

### Closing Thoughts

Engineering isn't just about writing code for a Jira ticket; it's about solving the small, personal anxieties of life through technology. Now, when I’m on vacation, I’ll know exactly what’s happening at home. Not because I’m checking a camera, but because my home is talking to me.

*If you're interested in building this yourself, feel free to reach out or check out the repository!*]]></content:encoded>
            <category>ESP32</category>
            <category>arduino</category>
            <category>hardware</category>
            <category>iot</category>
            <enclosure url="https://cdn.hashnode.com/uploads/covers/613c8b1b22b7a41dfe5fc089/9d526864-c838-4d42-b165-0ea8f94b7f20.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Debugging and Stopping Infinite Render Loops in React]]></title>
            <link>https://www.ravgeet.in/blog/debugging-and-stopping-infinite-render-loops-in-react</link>
            <guid>https://www.ravgeet.in/blog/debugging-and-stopping-infinite-render-loops-in-react</guid>
            <pubDate>Mon, 02 Feb 2026 10:30:01 GMT</pubDate>
            <description><![CDATA[Infinite renders are not magic bugs — they are deterministic feedback loops. Once you understand why a render retriggers itself, they become easy to reproduce, debug, and prevent.
This post walks through a step‑by‑step mental model to stop the “Maxim...]]></description>
            <content:encoded><![CDATA[Infinite renders are not magic bugs — they are deterministic feedback loops. Once you understand *why* a render retriggers itself, they become easy to reproduce, debug, and prevent.

This post walks through a **step‑by‑step mental model** to stop the “Maximum update depth reached“ errors for good.

## What an Infinite Render Loop Really Is

At its core, an infinite render loop looks like this:

1. Component renders
    
2. Some reactive logic runs (effect, watcher, computed, subscription)
    
3. That logic updates the state
    
4. State update causes a re-render
    
5. Repeat forever
    

The key insight:

> **Renders don’t loop by accident — they loop because state changes on every render.**

## The Fastest Way to Reproduce the Bug

When debugging, your first goal is **reproduction**.

### Minimal reproduction checklist

Comment out everything except one state value and one reactive hook (effect/watcher). Then add a log in both render and state update.

Example:

```javascript
function Component() {
  console.log('render');

  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log('effect');
    setCount(count + 1);
  }, [count]);
}
```

If you see:

```plaintext
render → effect → render → effect → ...
```

You’ve confirmed the loop.

## The #1 Root Cause: Unstable References

Most infinite loops come from **reference instability**, not logic errors.

### Identity vs Equality

```js
{} !== {}
[] !== []
() => {} !== () => {}
```

Even if values *look* equal, **their identity changes** on every render.

### Example: Object in dependencies

```javascript
function Component({ userId }) {
  const filters = { active: true, userId };

  useEffect(() => {
    fetchData(filters);
  }, [filters]); // ❌ new object every render
}
```

This results in the effect that runs forever.

## Stabilizing References with Memoization

### Fix with memoization

```javascript
const filters = useMemo(() => ({
  active: true,
  userId
}), [userId]);
```

Now, it has the same reference, but the effect runs only when `userId` changes. A rule of thumb is that if a variable goes into the dependency array, it must be referentially stable.

## Choosing Correct Dependencies (Not Fewer)

A common anti-pattern:

```javascript
useEffect(() => {
  if (status === 'loaded') return;
  setStatus('loaded');
}, [status]);
```

This effect updates its own dependency once, then converges to a stable state instead of looping forever.

## Linting That Actually Helps

Linting is one of the **cheapest ways** to prevent infinite render loops *before* they reach runtime — especially in React and Next.js apps.

By specifying the correct React Hooks Rules, you can catch the most common causes of render loops. BY using correct linting rules, you can enable rules in your editor. For example:

* `react-hooks/rules-of-hooks`
    
* `react-hooks/exhaustive-deps`
    

This forces correct dependency lists, exposes unstable references early, and prevents stale closures disguised as "fixes".

But still, the warnings about missing dependencies are not always true. It makes sense to treat them as **design bugs**, not suggestions.

## Debugging with `why-did-you-render` + LLMs

Although we are talking about this step at the very last, if time is crucial to you, then this could be the first resort.

Sometimes you *know* a component is re-rendering too much, but you don’t know **what changed**. Multiple states or effects might be responsible for an infinite render loop.

This is where `why-did-you-render` becomes extremely powerful — especially when paired with an LLM.

### What `why-did-you-render` does

`why-did-you-render` monkey-patches React in development and logs **exact reasons** for re-renders:

* Which props changed
    
* Whether the change was by **identity** or **value**
    
* Which hooks triggered the update
    

Instead of guessing, you get concrete evidence.

### Basic setup

```plaintext
[why-did-you-render]
MyComponent re-rendered because of props changes:
  props.filters changed
    prev: { active: true, userId: 1 }
    next: { active: true, userId: 1 }
  reason: props.filters !== prev.props.filters
```

This immediately tells you:

* The values are equal, but the **reference is not**
    
* Memoization is missing
    

### Feeding logs to an LLM (Copilot / ChatGPT)

Here’s where debugging gets *much* faster.

You can save the console logs as a `.log` file and feed them to the LLM agent along with the code context that you feel might be the reason for the infinite rendering. You can use the following prompt:

> "I have attached the console logs from why-did-you-render along with the code in which the infinite loop is happening. Can you find what is causing this behaviour and how do I stabilize it?"

Because the logs already encode **identity vs equality**, the LLM can:

* Identify unstable objects/functions
    
* Suggest correct `useMemo` / `useCallback` placement
    
* Detect unnecessary props drilling
    
* Recommend architectural fixes (lifting state, memo boundaries)
    

This removes the guesswork that usually slows humans down.

### Why this works so well with LLMs

LLMs struggle with *implicit runtime behavior*.

`why-did-you-render` turns runtime behavior into **explicit text**.

Once behavior is textual:

> Debugging becomes a reasoning problem — which LLMs are good at.

Used together, they form a tight loop:

1. Reproduce the render issue
    
2. Capture `why-did-you-render` logs
    
3. Paste logs + code into an LLM
    
4. Apply fix
    
5. Verify render stability
    

## Final Thought

Infinite render loops are not a framework flaw — they are a **signal**.

They tell you that:

* Data flow is unstable
    
* Identity is misunderstood
    
* Or side effects are misplaced
    

Once you respect reference stability and dependency correctness, infinite loops disappear — permanently.]]></content:encoded>
            <category>React</category>
            <category>JavaScript</category>
            <category>Next.js</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1770024886941/2dbeb986-7b5e-4af3-8d1e-c73c563744bc.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Rebuilding My Static Blog with Build-Time Data and Instant Search]]></title>
            <link>https://www.ravgeet.in/blog/rebuilding-my-static-blog-with-build-time-data-and-instant-search</link>
            <guid>https://www.ravgeet.in/blog/rebuilding-my-static-blog-with-build-time-data-and-instant-search</guid>
            <pubDate>Thu, 29 Jan 2026 18:30:00 GMT</pubDate>
            <description><![CDATA[Static sites are supposed to be fast, simple, and reliable. But over time, my personal blog started behaving like a dynamic app - runtime API calls, pagination logic everywhere, and fragmented view counts spread across platforms.
Last week, I rebuilt...]]></description>
            <content:encoded><![CDATA[Static sites are supposed to be fast, simple, and reliable. But over time, my personal blog started behaving like a dynamic app - runtime API calls, pagination logic everywhere, and fragmented view counts spread across platforms.

Last week, I rebuilt the blog section of **ravgeet.in** (Nuxt.js) to fix this properly. The end result is still a static site, but now it feels *alive*: aggregated view counts, instant search and sorting, and zero runtime dependencies on external APIs.

This post walks through the thinking, architecture, and trade-offs behind that rebuild.

## The problem with my old setup

Originally, my blog worked like this:

* Blog content lived on **Hashnode** (canonical source)
    
* Some posts were also cross-posted to **Dev.to**
    
* Pages fetched blog data **at runtime** using Hashnode’s GraphQL API
    
* Pagination logic (`hasNextPage`, cursors) lived inside the UI
    

This had a few downsides:

* A static site depending on live APIs felt wrong
    
* Local development and builds were slower and flaky
    
* Adding features like search or sorting would require more APIs
    

I wanted the blog to stay static - but smarter.

## Build-time data as a contract

The core decision was simple:

> **Move all external data fetching to build time, and treat the result as immutable static data.**

Instead of fetching blogs at runtime, I introduced a build step that:

1. Fetches blogs from Hashnode
    
2. Fetches articles from Dev.to
    
3. Matches the same article across platforms
    
4. Aggregates view counts
    
5. Writes everything into a single JSON file
    

At runtime, the site only reads from that JSON.

```plaintext
Hashnode + Dev.to
        ↓
Build-time fetch &amp; normalize
        ↓
static/blogs.json
        ↓
Nuxt UI (search, sort, views)
```

This one decision simplified everything else.

## Fetching and aggregating blog data

### Hashnode: canonical content

Hashnode remains the source of truth for:

* Title, slug, content, tags
    
* Publish date
    
* Cover image
    
* Base view count
    

I fetch all posts using Hashnode’s GraphQL API with pagination handled inside a Node.js script.

### Dev.to: distribution and extra reach

Dev.to is where additional readers come from, so ignoring those views felt wrong.

Using the Dev.to API (with a personal access token), I fetch all my articles and extract:

* `slug`
    
* `canonical_url`
    
* `page_views_count`
    

### Matching articles across platforms

This is the tricky part. Articles are matched using a layered strategy:

1. **Slug match**
    
2. **Canonical URL match**
    
3. **Title match**
    

Once matched, the final view count becomes:

```plaintext
combinedViews = hashnodeViews + devtoViews
```

The output for each blog includes:

* Combined views
    
* Platform-specific views (for debugging)
    
* Dev.to URL (if matched)
    

## Writing the static data contract

All processed data is written to the `static/blogs.json` file.

This file is:

* Generated at build time
    
* Git-ignored
    
* Treated as read-only by the app
    

It also includes metadata like the last updated time and the total blog count.

This JSON file effectively replaces my entire blog API.

## Replacing runtime APIs with static services

Previously, `services/blogs.js` made live GraphQL calls. After the refactor:

* The service dynamically imports `blogs.json`
    
* `find`, `findOne`, and `search` all operate locally
    
* No Axios
    
* No pagination state
    
* No network failures
    

From the UI’s perspective, nothing changed - but under the hood, everything became predictable.

## Instant search and sorting

Once all blog data is local, search becomes trivial.

I added:

* Client-side text search (title, brief, tags)
    
* Sorting by:
    
    * Published date (recent / oldest)
        
    * View count (most / least)
        

Because the dataset is small and static:

* Search results are instant
    
* No debouncing hacks
    
* No loading states
    
* Sorting is deterministic
    

This dramatically improves discoverability without introducing a search service.

## Trade-offs and lessons learned

This approach isn’t perfect:

* Build time increases slightly
    
* The JSON file grows over time
    
* It’s not suitable for real-time analytics
    

But for a personal blog, the trade-offs are worth it.

The key takeaways from the refactor that made me realize that:

* Static doesn’t mean lifeless
    
* Build-time data pipelines are underrated
    
* One clean data contract simplifies UI, UX, and performance
    

If you’re curious, the full implementation lives in the [ravgeet.in repository](https://github.com/ravgeetdhillon/ravgeet-web).]]></content:encoded>
            <category>Nuxt</category>
            <category>JavaScript</category>
            <category>Web Development</category>
            <category>website</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1769509123113/95179792-58e0-47aa-b33d-96fc716c0b99.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[My 7 Aspirations as a Software Engineer in 2026]]></title>
            <link>https://www.ravgeet.in/blog/my-7-aspirations-as-a-software-engineer-in-2026</link>
            <guid>https://www.ravgeet.in/blog/my-7-aspirations-as-a-software-engineer-in-2026</guid>
            <pubDate>Sun, 25 Jan 2026 06:30:37 GMT</pubDate>
            <description><![CDATA[2026 feels like a genuine inflection point in my software engineering journey. By February 2026, I will have completed four years as a professional engineer, starting with my first full-time role at CloudAnswers in February 2022. At this stage, my as...]]></description>
            <content:encoded><![CDATA[2026 feels like a genuine inflection point in my software engineering journey. By February 2026, I will have completed four years as a professional engineer, starting with my first full-time role at CloudAnswers in February 2022. At this stage, my aspirations are less about titles or rapid jumps and more about clarity — how I think, the kinds of problems I choose to solve, and the impact I want my work to have over time.

This post is not a checklist of goals. Instead, it’s a reflection on the *direction* I want my career to move in — as an engineer who values strong fundamentals, meaningful leverage, and sustainable, long-term growth.

## 1\. Mastering the Fundamentals

By 2026, my primary aspiration is to be **fundamentally strong**.

Not just "good at React" or "familiar with backend systems" but genuinely comfortable explaining *why* things behave the way they do:

* How JavaScript works under the hood
    
* How browsers render, schedule, and optimize work
    
* How React actually reconciles, schedules, and re-renders
    
* How data flows through a system end-to-end
    

I want to be the engineer who can debug issues calmly because I understand the system — not because I’ve memorized fixes.

## 2\. Thinking in Systems, Not Just Features

Another aspiration is to shift from **feature-level thinking** to **system-level thinking**.

Instead of asking:

> “How do I implement this requirement?”

I want my default question to be:

> “How does this decision affect the system six months from now?”

That means caring about:

* Trade-offs
    
* Maintainability
    
* Operational complexity
    
* Developer experience
    

Good engineers ship features. Great engineers design systems that *survive change*. This shift usually happens when you’re trusted to own a product end‑to‑end, responsible not just for fixing bugs or adding features, but for the long‑term health and evolution of the entire system.

## 3\. Becoming a Strong Communicator, Not Just a Coder

By 2026, I want my value to extend beyond code.

This includes:

* Explaining complex ideas clearly to other engineers
    
* Writing thoughtful PR descriptions and design docs
    
* Adding working evidence in the PRs in the form of videos and screenshots
    
* Helping juniors build correct mental models
    
* Disagreeing respectfully and productively
    

Software engineering is a team sport. Clear thinking is useless if it can’t be communicated.

## 4\. Building Leverage Through Tools and Automation

One of my strongest aspirations is to **build leverage**.

Instead of solving the same problems repeatedly, I want to:

* Automate workflows
    
* Build internal tools
    
* Create systems that scale my impact beyond my own output
    

Leverage is what separates engineers who work *hard* from engineers who work *effectively*.

## 5\. Developing Taste and Judgment

Technical skills can be learned. **Judgment takes time.**

By 2026, I want to develop a strong engineering taste:

* Knowing when *not* to over-engineer
    
* Knowing when technical debt is acceptable
    
* Choosing boring solutions when they’re the right ones
    

This kind of judgment only comes from reflection, mistakes, and intentional learning.

## 6\. Staying Curious Without Burning Out

Finally, I aspire to stay curious — but sustainable.

Not chasing every new framework, but:

* Learning deeply
    
* Picking tools intentionally
    
* Balancing ambition with health
    

Longevity matters. I want a career that compounds, not one that exhausts me early.

## 7\. Launching at Least One Production-grade App

By the end of 2026, I want to have launched **at least one app that real users can use** — not just a side project living on GitHub.

This aspiration is less about building a startup and more about **closing the loop** as an engineer. From idea to execution to feedback, I want to experience the full lifecycle:

* Identifying a real problem (even a small one)
    
* Designing a simple, opinionated solution
    
* Making trade-offs under real constraints
    
* Shipping, maintaining, and iterating based on usage
    

Building a personal app forces a different level of ownership. There is no product manager, no deadline imposed by someone else, and no ambiguity about responsibility. If something is broken, unclear, or poorly designed — it’s on me.

More importantly, it sharpens judgment. You quickly learn what *actually* matters to users, which abstractions are worth the cost, and which technical decisions age poorly once real usage begins.

## Closing Thoughts

My aspirations for 2026 are less about *where* I work and more about *how* I work and think.

If I can be a calmer problem-solver, a clearer thinker, and an engineer who builds systems that last — I’ll consider myself on the right path.

I plan to revisit this post at the end of the year to assess how closely my reality aligned with these intentions, honestly.]]></content:encoded>
            <category>#growth</category>
            <category>software development</category>
            <category>personal development</category>
            <category>engineering</category>
            <category>leadership</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1769247203368/313620ca-4f36-44d0-b5d0-429ab1f01b86.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Realtime Deploy Notifications in Next.js with Toasts]]></title>
            <link>https://www.ravgeet.in/blog/realtime-deploy-notifications-in-nextjs-with-toasts</link>
            <guid>https://www.ravgeet.in/blog/realtime-deploy-notifications-in-nextjs-with-toasts</guid>
            <pubDate>Sat, 24 Jan 2026 09:00:00 GMT</pubDate>
            <description><![CDATA[Ever deployed a new version of your app and wished your users got notified instantly while they’re still using the old one? In this post, I'll show you how I built a live build checker in my Next.js app that detects when a new deployment is live and ...]]></description>
            <content:encoded><![CDATA[Ever deployed a new version of your app and wished your users got notified **instantly** while they’re still using the old one? In this post, I'll show you how I built a **live build checker** in my Next.js app that detects when a new deployment is live and gently nudges users to refresh using a toast notification.

> Use case: Helpful for apps deployed on Vercel where static pages and edge functions make it easy to serve outdated content across sessions.

## The Stack

* **Next.js 15**
    
* **React 19**
    
* **React-Bootstrap**
    
* **React-Toastify**
    
* **Vercel Edge Functions**
    
* **Supabase Auth (optional)**
    

## The Concept

When a new build is deployed, a unique timestamp (`NEXT_PUBLIC_BUILD_TIMESTAMP`) is injected into the environment. On the client, we **poll an API endpoint** every few minutes to check if the server's build timestamp has changed. If it has, we notify the user.

## Step-by-Step Implementation

### 1\. Add a build timestamp during build time

In `next.config.ts`:

```ts
const now = new Date().toISOString();

const nextConfig = {
  env: {
    NEXT_PUBLIC_BUILD_TIMESTAMP: now,
  },
};

export default nextConfig;
```

This ensures every build has a unique timestamp baked in at compile time.

### 2\. Create a `/api/build` route

This edge function returns the current server build timestamp:

```ts
import { NextRequest } from "next/server";
export const runtime = "edge";

export async function GET(request: NextRequest) {
  return new Response(
    JSON.stringify({ build: process.env.NEXT_PUBLIC_BUILD_TIMESTAMP }),
    {
      headers: { "Content-Type": "application/json" },
    }
  );
}
```

You can also add authentication here if needed, e.g., for private dashboards.

### 3\. Build a custom hook: `useLiveBuildChecker`

This hook polls the `/api/build` endpoint periodically and triggers a toast if it detects a new version:

```ts
import { useEffect, useRef } from "react";
import { toast } from "react-toastify";
import axios from "axios";

export function useLiveBuildChecker(intervalMin = 5) {
  const currentBuild = useRef(
    process.env.NEXT_PUBLIC_BUILD_TIMESTAMP
  );

  useEffect(() => {
    const checkForUpdate = async () => {
      try {
        const res = await axios.get("/api/build");
        const { build } = res.data;

        if (build &amp;&amp; build !== currentBuild.current) {
          toast.info("A new version of this page is available. Refresh to see the latest changes.", {
            autoClose: false,
            position: "bottom-right",
          });
          clearInterval(interval); // stop further polling
        }
      } catch (e) {
        console.error("Update check failed:", e);
      }
    };

    const interval = setInterval(
      checkForUpdate,
      process.env.NODE_ENV === "development" ? 5 * 1000 : intervalMin * 60 * 1000
    );

    return () => clearInterval(interval);
  }, [intervalMin]);
}
```

### 4\. Add Toast UI + Hook in Layout

Update your app layout to include the `ToastContainer` and run the hook:

```tsx
"use client";

import { ReactNode } from "react";
import { ToastContainer } from "react-toastify";
import { useLiveBuildChecker } from "@/hooks/useLiveBuildChecker";

import "react-toastify/dist/ReactToastify.css";

interface Props {
  children: ReactNode;
}

export default function AppLayout({ children }: Props) {
  useLiveBuildChecker(); // default 5-min interval

  return (
    <>
      {children}
      <ToastContainer />
    </>
  );
}
```

## Result

Whenever a new version of your app is deployed, users still active in the browser will get this neat toast:

> **"**A new version of this page is available. Refresh to see the latest changes.**"**

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1769244335784/ba5fed26-3f32-4300-8a6c-53b504f6bd0f.png align="center")

## Why This Works

This approach is fully **edge-friendly**, **stateless**, and **easy to scale**. And unlike service worker-based update detection, it works great for **SSR/ISR setups** and **custom dashboards**.

## Bonus Ideas

* You can trigger a background update via service workers.
    
* You can track how long users stay on an outdated version.
    

## Final Thoughts

Keeping users on the latest version of your app improves stability, security, and experience. With just a few lines of code, you can achieve this kind of real-time deploy awareness in any Next.js app.

If you're building a dashboard, personal tool, or even a SaaS product, this is one of those *delightfully simple yet powerful* improvements.]]></content:encoded>
            <category>React</category>
            <category>Next.js</category>
            <category>SaaS</category>
            <category>JavaScript</category>
            <category>product development</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1769245089949/ae0d75b0-a29b-414b-adb5-5e0bbc9ad823.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[How I Built a Unified Calendar Dashboard with Next.js, Vercel Edge Functions & No Database]]></title>
            <link>https://www.ravgeet.in/blog/how-i-built-a-unified-calendar-dashboard-with-nextjs-vercel-edge-functions-and-no-database</link>
            <guid>https://www.ravgeet.in/blog/how-i-built-a-unified-calendar-dashboard-with-nextjs-vercel-edge-functions-and-no-database</guid>
            <pubDate>Tue, 04 Nov 2025 12:58:35 GMT</pubDate>
            <description><![CDATA[Problem
I was juggling tasks across:

Company ClickUp (for team collaboration)

Notion (for personal to-dos and planning)

Google Calendar (from both company & personal emails)


The chaos was real. I was missing due dates, spending too much time jum...]]></description>
            <content:encoded><![CDATA[## Problem

I was juggling tasks across:

* Company ClickUp (for team collaboration)
    
* Notion (for personal to-dos and planning)
    
* Google Calendar (from both company &amp; personal emails)
    

The chaos was real. I was missing due dates, spending too much time jumping between apps, and lacked a single place to glance at all my tasks.

## The Solution

I built a **read-only personal dashboard** that:

* Aggregates tasks/events from ClickUp, Notion, and Google Calendar
    
* Groups tasks as **Overdue**, **Due Today**, **Upcoming by Date,** and **No Due Date**
    
* Runs entirely on **Next.js + Edge Functions**
    
* Uses **no database**, just live API reads
    
* Stores my daily tasks that I need to do
    
* Is **password protected** and deployed on a Vercel subdomain
    

Here’s how it looks after multiple polishes:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1762317912196/cd3dabc8-6e00-4f7c-9aac-12d07bd79b02.png align="center")

## Tech Stack

* **Frontend**: Next.js App Router + React Bootstrap + TanStack Query
    
* **API Layer**: Vercel Edge Functions
    
* **Auth**: Cookie-based with middleware protection
    
* **Hosting**: Vercel subdomain
    

## Core Features

### Unified Task View

Each task is grouped into:

* Overdue
    
* Due Today / Tomorrow
    
* Upcoming
    
* No Due Date
    

It pulls data from these 3 APIs:

```ts
const [clickup, notion, calendar] = await Promise.all([
  fetchClickupTasks(),
  fetchNotionTasks(),
  fetchCalendarEvents(),
]);
```

### Auth with Middleware

I implemented simple cookie-based authentication to protect my dashboard. The middleware runs on every request and checks for a valid auth cookie before allowing access to protected routes.

```ts
// middleware.ts
import { NextRequest, NextResponse } from "next/server";

export function middleware(request: NextRequest) {
  const auth = request.cookies.get("auth");
  const pathname = request.nextUrl.pathname;

  const publicPaths = ["/login", "/api/login", "/api/logout"];
  if (publicPaths.includes(pathname)) return NextResponse.next();
  if (auth?.value === "1") return NextResponse.next();

  if (pathname.startsWith("/api/")) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  return NextResponse.redirect(new URL("/login", request.url));
}

export const config = {
  matcher: [
    "/",
    "/dashboard",
    "/api/events",
    "/api/clickup",
    "/api/notion",
    "/api/calendar",
  ],
};
```

### Modular API Fetchers

I kept the codebase clean by separating each API integration into its own module. This makes it easier to maintain and test each data source independently.

```ts
// lib/sources/clickup.ts
export async function fetchClickupTasks() {
  const res = await fetch("https://api.clickup.com/api/v2/...");
  return await res.json();
}
```

The same goes for Notion &amp; Google Calendar.

### Server API Route Example

The backend API routes handle data aggregation from all sources. This route fetches tasks from all three platforms simultaneously and returns them as a unified JSON response.

```ts
// app/api/events/route.ts
import { getAllEvents } from "@/lib/getAllEvents";

export async function GET() {
  const { clickup, notion, calendar } = await getAllEvents();
  return Response.json({ clickup, notion, calendar });
}
```

## Frontend UI

The frontend uses TanStack Query for efficient data fetching with automatic caching and background updates. This ensures the dashboard stays responsive while keeping data fresh.

Using TanStack Query for live fetching and caching:

```ts
const { data, isLoading } = useQuery({
  queryKey: ["events"],
  queryFn: () => fetch("/api/events").then((res) => res.json()),
});
```

Then we categorize tasks by due date:

```ts
const overdue = allTasks.filter(task => isBefore(task.dueDate, today));
const dueToday = allTasks.filter(task => isToday(task.dueDate));
const upcoming = groupByDate(allTasks.filter(...));
const noDueDate = allTasks.filter(task => !task.dueDate);
```

The dashboard also includes a "Today's Work List" feature where I can curate specific tasks from across all platforms. This has become my morning ritual - selecting what I want to focus on for the day creates clarity and intention around my daily goals.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1762259973414/af1f68b6-cb04-4746-b7f7-d9025de8ed09.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1762259962064/435daf88-ef06-490e-bdd4-15633323ec2c.png align="center")

## Deployment

I deployed the app to Vercel and created a subdomain via Hostinger by:

1. Creating a subdomain DNS record
    
2. Adding the domain to Vercel
    
3. Setting env variables and password via the Vercel dashboard
    

No secrets or tasks are stored — it's 100% live.

## What’s Next?

I'm happy with the current version, but I could add the following features in the future:

* Month View toggle
    
* Desktop notifications for overdue tasks
    
* Auto-refresh every 10 mins
    
* Tauri or Expo wrapper for mobile
    

## Final Thoughts

This project helped me regain clarity over my weekly tasks. I have pinned this dashboard in my browser and open it every morning to immediately see what matters. It's fast, reliable, and mine.

If you're tired of hopping between 5 apps, build something simple that fits your brain.]]></content:encoded>
            <category>General Programming</category>
            <category>app development</category>
            <category>React</category>
            <category>JavaScript</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1762260694520/c760b5d3-bd6e-42d1-b954-495840544610.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Building a Smart Hardware Inventory System That Actually Works]]></title>
            <link>https://www.ravgeet.in/blog/building-a-smart-hardware-inventory-system-that-actually-works</link>
            <guid>https://www.ravgeet.in/blog/building-a-smart-hardware-inventory-system-that-actually-works</guid>
            <pubDate>Sun, 12 Oct 2025 18:30:43 GMT</pubDate>
            <description><![CDATA[We've all been there. You're rushing to meet a deadline and desperately need that specific USB drive—the one with the 32GB capacity that has your client's backup files. But as you stare at the drawer full of identical-looking pendrives, cables, and c...]]></description>
            <content:encoded><![CDATA[We've all been there. You're rushing to meet a deadline and desperately need that specific USB drive—the one with the 32GB capacity that has your client's backup files. But as you stare at the drawer full of identical-looking pendrives, cables, and chargers, you realize you have no idea which one is which.

Last month, I finally got tired of this digital scavenger hunt and decided to build something better. Not an enterprise-grade asset management system (who has time for that?), but a lightweight, practical solution that would actually solve my real-world problem.

Here's how I built a hardware inventory system that's simple enough to maintain and smart enough to find anything instantly.

## The Problem: Hardware Chaos

My desk drawer looked like a tech graveyard. Multiple USB drives, various charging cables, adapters, and dongles—all visually identical but functionally different. The 8GB drive with personal photos looked exactly like the 64GB one with work projects. The USB-C cable that supports fast charging was indistinguishable from the data-only one.

Every time I needed something specific, I'd end up:

* Plugging in random drives to check their contents
    
* Testing cables to see what they actually do
    
* Wasting 10-15 minutes on something that should take 30 seconds
    

There had to be a better way.

## The Solution: Smart Simplicity

Instead of over-engineering this, I decided to build something that would work with tools I already use daily. My requirements were simple:

1. **Quick to update** - Adding new items shouldn't be a chore
    
2. **Accessible anywhere** - No app installations or complex logins
    
3. **Physical integration** - Must work with actual hardware, not just digital records
    
4. **Intelligent search** - Find items by description, not just exact matches
    

## Building the System: A Step-by-Step Breakdown

### Step 1: The Foundation - Google Sheets as a Database

I started with a clean Google Sheet with these columns:

* **ID**: Auto-generated unique identifier
    
* **Type**: Category like "USB Drive", "Cable", "Charger"
    
* **Description**: The magic field where I describe each item in plain English
    
* **Date Added**: Automatic timestamp
    
* **Status**: Available, In Use, Lost
    

Nothing revolutionary here, but the key was keeping it simple enough that I'd actually maintain it.

### Step 2: Smart ID Generation with Apps Script

Manual ID assignment? No thanks. I wrote a Google Apps Script function that generates short, unique IDs automatically:

```javascript
function generateUniqueID() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const existingIDs = sheet.getRange('A:A').getValues().flat();
  
  let newID;
  do {
    newID = Math.random().toString(36).substring(2, 5).toUpperCase();
  } while (existingIDs.includes(newID));
  
  return newID;
}
```

This creates memorable 3-character IDs like `XR7` or `K2M`. Short enough to write on tiny labels, unique enough to avoid conflicts.

### Step 3: Bridging Digital and Physical Worlds

Here's where it gets practical. I ordered a pack of small adhesive labels and a fine-tip permanent marker. Every time I add a new item to the sheet:

1. The system generates a unique ID
    
2. I write that ID on a physical label
    
3. I stick the label directly on the hardware
    

Now every pendrive, cable, and charger has its own "name tag." When I need something, I just check the physical tag and look it up instantly.

**Pro tip**: For items too small for labels (like tiny dongles), I use small zip-lock bags with labeled sticky notes.

### Step 4: Web Access Without the Hassle

Opening Google Sheets every time felt clunky. Instead, I:

1. Published the sheet as a web app through Apps Script
    
2. Set up a custom subdomain using Vercel
    
3. Created a clean, mobile-friendly interface
    

Now I can check my inventory from my phone while standing in front of my hardware drawer. Game-changer.

### Step 5: AI-Powered Search That Actually Understands

This is where the system gets genuinely smart. Instead of remembering exact product names or scrolling through rows, I integrated OpenAI search that understands natural language queries:

* *"Find the Kingston drive with project files"*
    
* *"Which cable charges my laptop?"*
    
* *"Show me USB drives over 16GB"*
    

The AI reads through all my descriptions and instantly returns the matching items with their IDs. It's like having a personal assistant for my hardware drawer.

## Real-World Impact: Why This Actually Works

**Before**: "I need that specific USB drive... *proceeds to test 6 different drives*"

**After**: "I need that specific USB drive" → Check AI search → "It's the one labeled K2M" → Done in 30 seconds.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1759218497213/0e38e669-e964-454d-aedf-fbedf2bdb230.png align="center")

The system works because it addresses the actual problem, not the theoretical one. I don't need enterprise features like check-out workflows or depreciation tracking. I just need to find my stuff quickly.

### The Numbers

After 3 months of use:

* **45 items** tracked (drives, cables, adapters, dongles)
    
* **Average search time**: 15 seconds (down from 10+ minutes)
    
* **Time to add new item**: 90 seconds
    

## Lessons Learned

### What Works Really Well

* **Physical labels are non-negotiable** - Digital-only systems fail in the real world
    
* **AI search is a multiplier** - Turns a simple spreadsheet into something genuinely intelligent
    
* **Simplicity scales** - Started with pendrives, now tracks everything
    

## What's Next: Future Improvements

The foundation is solid, so I'm adding features that solve real pain points:

**QR Code Integration**: Generate QR codes for each item that link directly to their details. Scan with phone → instant access.

**Capacity Analytics**: Total up storage capacity across all drives, and identify gaps in my hardware collection.

## The Bigger Picture

This tiny project reminded me why I love building solutions to real problems. Not every system needs machine learning, microservices, or a dedicated mobile app. Sometimes, Google Sheets, a bit of scripting, and some creativity are exactly the right tools.

The best productivity systems are the ones you actually use. And for keeping track of my ever-growing collection of tech accessories, this simple approach has been perfect.]]></content:encoded>
            <category>Productivity</category>
            <category>openai</category>
            <category>Programming Blogs</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1759218185506/774ed865-391e-45b2-a959-e2a106cc230c.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Building a Basic AI Agent]]></title>
            <link>https://www.ravgeet.in/blog/building-an-ai-powered-function-orchestrator</link>
            <guid>https://www.ravgeet.in/blog/building-an-ai-powered-function-orchestrator</guid>
            <pubDate>Thu, 02 Oct 2025 12:30:19 GMT</pubDate>
            <description><![CDATA[As developers, we constantly face a dilemma: how do we make our code flexible enough to handle natural human requests without hardcoding every possible scenario?
While working on various automation projects, I kept running into the same pattern—users...]]></description>
            <content:encoded><![CDATA[As developers, we constantly face a dilemma: **how do we make our code flexible enough to handle natural human requests without hardcoding every possible scenario?**

While working on various automation projects, I kept running into the same pattern—users would ask for something in plain English, like *"Can you calculate 2+25-4^2?* or *"Process these files and send me a summary",* but my code was rigid, expecting specific formats and predefined workflows.

The breakthrough came when I realized: **What if AI handled the planning, and I just focused on building solid, reusable functions?** Instead of trying to anticipate every user request, let AI interpret the intent and dynamically orchestrate my functions.

This post walks through building a mini AI agent framework that separates **what you can do** (functions) from **how to do it** (AI planning). The result? Code that adapts to human intent rather than forcing humans to adapt to your interface.

## The Problem: Traditional Code vs. Human Intent

How many times have you written code that looks like this?

```python
def complex_math_solver(expression):
    # Parse expression
    # Apply PEMDAS rules
    # Handle edge cases
    # Return result
    pass
```

The problem? You're cramming **parsing logic**, **mathematical rules**, and **execution** into one monolithic function. What if we could separate these concerns entirely?

## The Solution: AI as a Function Orchestrator

Instead of hardcoding business logic, what if we let AI handle the **planning** while we focus on building **atomic, reusable functions**?

Here's the architecture:

### Step 1: Define Atomic Functions

First, we create simple, single-purpose functions that do one thing well. Think of these as your building blocks—each function is pure, testable, and completely independent. The key is keeping them atomic so AI can combine them in any order to solve complex problems.

```python
def sum(a, b):
    return a + b

def multiply(a, b):
    return a * b

def power(a, b):
    return a ** b

# Function registry for dynamic execution
FUNCTIONS = {
    "sum": sum,
    "multiply": multiply,
    "power": power,
}
```

### Step 2: Document Your Functions (For AI)

Next, we create clear documentation for each function. This isn't just good practice — it's essential for AI to understand what each function does and how to use it. Think of this as your function's "instruction manual" that AI reads to make smart planning decisions.

```python
DOCS = {
    "sum": {
        "description": "Add two numbers",
        "args": {
            "a": {
                "type": "number",
                "description": "a float or int number",
            },
            "b": {
                "type": "number",
                "description": "a float or int number",
            },
        },
    },
    "multiply": {
        "description": "Multiply two numbers",
        "args": {
            "a": {
                "type": "number",
                "description": "a float or int number",
            },
            "b": {
                "type": "number",
                "description": "a float or int number",
            },
        },
    },
    "power": {
        "description": "Raise a to the power of b",
        "args": {
            "a": {
                "type": "number",
                "description": "a float or int number",
            },
            "b": {
                "type": "number",
                "description": "a float or int number",
            },
        },
    },
}
```

### Step 3: Let AI Generate Execution Plans

Finally, we let AI do the heavy lifting — interpreting natural language requests and creating step-by-step execution plans. The AI uses your function documentation to understand what's possible, then figures out the optimal sequence to achieve the user's goal.

```python
def get_action_plan(user_query):
    prompt = f"""
    You are an AI planner. Convert the user's request into a step-by-step plan.

    Available functions:
    {json.dumps(DOCS, indent=2)}

    User's query: "{user_query}"

    Return a JSON plan with ordered steps.
    """

    response = openai.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": prompt}],
        temperature=0
    )
    return json.loads(response.choices[0].message.content)
```

## Real Example: "Solve 2+2\*5+4^2"

When a user sends this request to the system, it gets passed to OpenAI along with the function documentation. The AI analyzes the mathematical expression, applies PEMDAS rules, and returns a structured JSON plan that breaks down the calculation into atomic steps using the available functions.

**Input:** Natural language request  
**AI Output:** Structured execution plan

```json
{
  "steps": [
    { "function": "power", "args": { "a": 4, "b": 2 } },
    { "function": "multiply", "args": { "a": 2, "b": 5 } },
    { "function": "sum", "args": { "a": 2, "b": "<result_of_step_2>" } },
    {
      "function": "sum",
      "args": { "a": "<result_of_step_3>", "b": "<result_of_step_1>" }
    }
  ]
}
```

**Execution Output:**

```plaintext
Step 1: power(4, 2) → 16
Step 2: multiply(2, 5) → 10
Step 3: sum(2, 10) → 12
Step 4: sum(12, 16) → 28

Final Result: 28
```

## The Magic: Dynamic Plan Execution

This function takes the AI-generated plan and executes it step by step. The key insight is **result chaining**—each step can reference outputs from previous steps using placeholders like `<result_of_step_1>`. The system automatically resolves these references, creating a dynamic pipeline where complex calculations emerge from simple function compositions.

```python
def execute_plan(plan):
    results = {}
    for idx, step in enumerate(plan["steps"], start=1):
        func_name = step["function"]
        args = step["args"]

        # Replace placeholders with previous results
        for k, v in args.items():
            if isinstance(v, str) and v.startswith("<result_of_step_"):
                step_idx = int(v.split("_")[-1].replace(">", ""))
                args[k] = results[step_idx]

        # Execute function dynamically
        func = FUNCTIONS[func_name]
        result = func(**args)
        results[idx] = result

        print(f"Step {idx}: {func_name}({args}) → {result}")

    return results[len(results)]
```

## Why This Architecture Wins

### **Separation of Concerns**

* **You write:** Pure, testable functions
    
* **AI handles:** Complex planning and orchestration
    
* **System manages:** Execution flow and state
    

### **Human-in-the-Loop Safety**

```python
if "error" in plan:
    print("❌ Error in plan generation:")
    print(plan["error"]["message"])
    sys.exit(1)  # Safe exit on planning failures
```

For example, if a user asks *"Divide 10 by 0 and add 5"*, the AI can detect this is mathematically impossible and return an error response like:

```json
{
  "error": {
    "message": "Cannot divide by zero - this operation is undefined in mathematics"
  }
}
```

This prevents dangerous operations from executing and provides clear feedback to users.

### **Infinite Extensibility**

Today, it's math functions:

```python
FUNCTIONS = {
    "sum": sum,
    "multiply": multiply,
    "divide": divide
}
```

Tomorrow it could be **anything**:

```python
FUNCTIONS = {
    "read_file": read_file,
    "send_email": send_email,
    "query_database": query_db,
    "fetch_weather": weather_api,
    "analyze_sentiment": sentiment,
}
```

## Real-World Applications

### File Operations

*"Take all .txt files in /docs, extract headings, and create a summary document"*

**Required Functions:** `list_files()`, `read_file()`, `extract_headings()`, `create_document()`, `write_file()`

### API Orchestration

*"Get weather for New York, if it's raining, send a Slack message to #general"*

**Required Functions:** `fetch_weather()`, `check_condition()`, `send_slack_message()`

### Data Pipeline

*"Load sales.csv, calculate monthly averages, generate a chart, and email it to the team"*

**Required Functions:** `load_csv()`, `calculate_average()`, `group_by_month()`, `generate_chart()`, `send_email()`

### MCP Server Integration

*"Query our customer database, analyze sentiment of recent feedback, and create a dashboard"*

**Required Functions:** `query_database()`, `analyze_sentiment()`, `aggregate_data()`, `create_dashboard()`, `save_report()`

## The Bigger Picture

This isn't just a math solver — it's a **mini AI agent framework**. The system provides:

1. **Function Library:** Atomic, reusable components
    
2. **AI Planner:** Intelligent request interpretation
    
3. **Execution Engine:** Safe, traceable function orchestration
    
4. **Human Oversight:** Approval and error handling
    

## What's Next?

1. **Add more function types** (file ops, API calls)
    
2. **Implement approval workflows** (show plan before execution)
    
3. **Add function validation** (type checking, parameter validation)
    
4. **Build a web interface** (make it accessible to non-developers)
    
5. **Integrate with MCP servers** (extend to complex business logic)
    

**The bottom line:** Stop hardcoding business logic. Let AI handle the planning, you handle the implementation. The result? More flexible, maintainable, and extensible code that adapts to user intent rather than forcing users to adapt to your interface.]]></content:encoded>
            <category>AI</category>
            <category>agentic AI</category>
            <category>openai</category>
            <category>Programming Blogs</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1759051142066/7eae6181-9d78-479c-b91e-f39a41468d4a.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[The ultimate guide to Python logging]]></title>
            <link>https://www.ravgeet.in/blog/the-ultimate-guide-to-python-logging</link>
            <guid>https://www.ravgeet.in/blog/the-ultimate-guide-to-python-logging</guid>
            <pubDate>Tue, 19 Aug 2025 18:30:29 GMT</pubDate>
            <description><![CDATA[When an application runs, it performs a tremendous number of tasks, with many happening behind the scenes. Even a simple to-do application has more than you'd expect. The app will at a bare minimum have tons of tasks like user logins, creating to-dos...]]></description>
            <content:encoded><![CDATA[When an application runs, it performs a tremendous number of tasks, with many happening behind the scenes. Even a simple to-do application has more than you'd expect. The app will at a bare minimum have tons of tasks like user logins, creating to-dos, updating to-dos, deleting to-dos, and duplicating to-dos. The tasks an application performs can result in success or potentially result in some errors.

For anything you're running that has users, you'll need to at least consider monitoring events happening so that they can be analyzed to identify bottlenecks in the performance of the application. This is where **logging** is useful. Without logging, it's impossible to have insight or observability into what your application is actually doing.

In this article, you'll learn how to create logs in a Python application using the Python logging module. Logging can help Python developers of all experience levels develop and analyze an application's performance more quickly. Let's dig in!

Read the full blog on [Honeybadger](https://www.honeybadger.io/blog/python-logging/).

%[https://www.honeybadger.io/blog/python-logging/] 

Thanks for reading 💜

---

[I publish a monthly newsletter in which](https://www.tiny.cloud/) I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come ac[ross while surfing the web.](https://www.tiny.cloud/)

[Connect with](https://www.tiny.cloud/) [me through Twitter • LinkedIn • Github or](https://www.tiny.cloud/) send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>Python</category>
            <category>Logs</category>
            <category>Programming Blogs</category>
            <category>General Programming</category>
            <category>Web Development</category>
        </item>
        <item>
            <title><![CDATA[Working with Markdown in Python]]></title>
            <link>https://www.ravgeet.in/blog/working-with-markdown-in-python</link>
            <guid>https://www.ravgeet.in/blog/working-with-markdown-in-python</guid>
            <pubDate>Thu, 07 Aug 2025 06:30:29 GMT</pubDate>
            <description><![CDATA[If you use the Internet, you have surely come across the term Markdown. Markdown is a lightweight markup language that makes it very easy to write formatted content. It was created by John Gruber and Aaron Swartz in 2004. It uses very easy-to-remembe...]]></description>
            <content:encoded><![CDATA[If you use the Internet, you have surely come across the term **Markdown.** [Markdown](https://daringfireball.net/projects/markdown/) is a lightweight markup language that makes it very easy to write formatted content. It was created by John Gruber and Aaron Swartz in 2004. It uses very easy-to-remember syntax and is therefore used by many bloggers and content writers around the world. Even this blog that you are reading is written and formatted using Markdown.

Markdown is one of the most widely used formats for storing formatted [data. It](https://daringfireball.net/projects/markdown/) easily integrates with Web technologies, as it can be converted to HTML or vice versa using Markdown compilers. It allows you to write HTML entities, such as headings, lists, images, links, tables, and more without much effort or code. It is used in blogs, content management systems, Wikis, documentation, and many more places.

In this article, you'll learn how to work with Markdown in a Python ap[plicatio](https://daringfireball.net/projects/markdown/)n using different Python packages, including markdown, front matter, and markdownify.

Read the full blog on [Honeybadger](https://www.honeybadger.io/blog/python-markdown/).

%[https://www.honeybadger.io/blog/python-markdown/] 

Thanks for reading 💜

---

[I publish a monthly newsletter in which](https://www.tiny.cloud/) I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come ac[ross while surfing the web.](https://www.tiny.cloud/)

[Connect with](https://www.tiny.cloud/) [me through Twitter • LinkedIn • Github or](https://www.tiny.cloud/) send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>Python</category>
            <category>Programming Blogs</category>
            <category>markdown</category>
            <category>automation</category>
        </item>
        <item>
            <title><![CDATA[Handling undo functions in rich text editors]]></title>
            <link>https://www.ravgeet.in/blog/handling-undo-functions-in-rich-text-editors</link>
            <guid>https://www.ravgeet.in/blog/handling-undo-functions-in-rich-text-editors</guid>
            <pubDate>Tue, 29 Jul 2025 18:30:16 GMT</pubDate>
            <description><![CDATA[Undo and redo operations are a must-have feature in any rich text editor – they’re a user's safety net. For a great user experience (UX), users need to solve their editing problems in a rich text editor.
An undo/redo button makes your users more conf...]]></description>
            <content:encoded><![CDATA[Undo and redo operations are a must-have feature in any rich text editor – they’re a user's safety net. For a great user experience (UX), users need to solve their editing problems in a rich text editor.

An undo/redo button makes your users more confident, because it’s a clear signal that if they make a mistake, they can easily undo and restore changes. For example, if a user accidentally deletes a paragraph, the undo function can restore their work – and spare them a lot of frustration.

However, implementing the undo/redo functionality is complicated. It requires an understanding of [**data structures like Stack**](https://www.geeksforgeeks.org/stack-data-structure/).

You need to know which actions need to be pushed onto the stack, as well as when to push them, and the same goes for the pop operation.

In this article, you'll find out about the complexity of creating and maintaining the undo/redo functionality, and see how the [**TinyMCE rich text editor**](https://www.tiny.cloud/tinymce/) makes it easy.

Read the full blog on [Tiny](https://www.tiny.cloud/blog/undo-function-handling/).

%[https://www.tiny.cloud/blog/undo-function-handling/] 

Thanks for reading 💜

---

[I publish a monthly newsletter in which](https://www.tiny.cloud/) I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come ac[ross while surfing the web.](https://www.tiny.cloud/)

[Connect with](https://www.tiny.cloud/) [me through Twitter • LinkedIn • Github or](https://www.tiny.cloud/) send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>Web Development</category>
            <category>React</category>
            <category>Frontend Development</category>
            <category>JavaScript</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1750429199494/62a8139a-dc56-4364-ac06-4587c9e57daf.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[How to enable in-app Notifications using TinyMCE APIs]]></title>
            <link>https://www.ravgeet.in/blog/how-to-enable-in-app-notifications-using-tinymce-apis</link>
            <guid>https://www.ravgeet.in/blog/how-to-enable-in-app-notifications-using-tinymce-apis</guid>
            <pubDate>Wed, 16 Jul 2025 18:30:44 GMT</pubDate>
            <description><![CDATA[Notifications add value to an app by helping to build conversations between users. They can also help make an interface less hostile by sharing important information. And it’s because they’re so useful that you’re being flooded by feature requests th...]]></description>
            <content:encoded><![CDATA[Notifications add value to an app by helping to build conversations between users. They can also help make an interface less hostile by sharing important information. And it’s because they’re so useful that you’re being flooded by feature requests that are asking for notifications to be added to your app. Now (please).

However, because of the noise, it’s hard to set aside the time to figure out the necessary requirements: does it need an opt-in and opt-out feature? Audience segmentation? Personalization? 

The work involved just keeps piling up. 

The good news is that in-app notifications (the kind of notifications that give useful information on screen after a change or event), are easy to handle with a [**reliable rich text editor such as T**](https://www.tiny.cloud/)[**inyMCE**. The editor has a notifications AP](https://www.tiny.cloud/)I that can communicate vital information to the user, and is easy and quick to set up. In this article, you'll find a guide on how to set up and manage notifications in the TinyMCE rich text editor, with NotificationManager API.

Read the full blog on [Tiny](https://www.tiny.cloud/blog/enable-in-app-notifications/).

%[https://www.tiny.cloud/blog/enable-in-app-notifications/] 

Thanks for reading 💜

---

[I publish a monthly newsletter in which](https://www.tiny.cloud/) I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come ac[ross while surfing the web.](https://www.tiny.cloud/)

[Connect with](https://www.tiny.cloud/) [me through Twitter • LinkedIn • Github or](https://www.tiny.cloud/) send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>General Programming</category>
            <category>React</category>
            <category>Web Development</category>
            <category>Text Editors</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1750429108540/ee479c2d-5006-489f-bc2a-03cb8395da7a.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[Introducing Ravgeek: Dev Concepts in 60 Seconds]]></title>
            <link>https://www.ravgeet.in/blog/introducing-ravgeek-dev-concepts-in-60-seconds</link>
            <guid>https://www.ravgeet.in/blog/introducing-ravgeek-dev-concepts-in-60-seconds</guid>
            <pubDate>Wed, 09 Jul 2025 12:32:11 GMT</pubDate>
            <description><![CDATA[After years of writing code, debugging endlessly, and explaining APIs to teammates over coffee, I’ve finally taken the plunge into something new — bite-sized developer explainers on YouTube.
📺 My new channel is called Ravgeek (“t” dropped from my na...]]></description>
            <content:encoded><![CDATA[After years of writing code, debugging endlessly, and explaining APIs to teammates over coffee, I’ve finally taken the plunge into something new — **bite-sized developer explainers on YouTube**.

📺 My new channel is called [**Ravgeek**](https://www.youtube.com/@ravgeek) (“t” dropped from my name)— and it's built around a simple idea:

> **Make technical concepts simple, fun, and fast.**

Whether it’s understanding what a REST API is, how Git works, or when to use GraphQL, each video is designed to explain core ideas in **under 60 seconds** — in a way that’s accessible to beginners and still fun for experienced devs.

Here’s a video in which I explain - “What is OAuth”:

%[https://www.youtube.com/watch?v=zZKV-w31W7A] 

You’ll see:

* ⚡️ Rapid, to-the-point explanations
    
* 🎙️ Conversational storytelling (think devs talking over chai)
    
* 🎨 Animations, avatars, and a touch of humor
    

This has been a passion project for me — combining my love for **coding, storytelling, and design** — and I’m excited to finally share it with the world.

👉 Check out the channel: [youtube.com/@ravgeek](https://www.youtube.com/@ravgeek)  
💬 And if you like what you see, hit that subscribe button and let me know what topic you'd like me to cover next.

Let’s learn, laugh, and geek out together.]]></content:encoded>
            <category>AI</category>
            <category>personal development</category>
            <category>youtube</category>
            <category>content creation</category>
        </item>
        <item>
            <title><![CDATA[Building a Smart Session Tracker for Your Mac's Menu Bar]]></title>
            <link>https://www.ravgeet.in/blog/building-a-smart-session-tracker-for-your-macs-menu-bar</link>
            <guid>https://www.ravgeet.in/blog/building-a-smart-session-tracker-for-your-macs-menu-bar</guid>
            <pubDate>Wed, 09 Jul 2025 06:30:34 GMT</pubDate>
            <description><![CDATA[Picture this: You sit down at your Mac with a coffee, planning to "quickly check a few emails." Next thing you know, it's 3 PM, your coffee has achieved room temperature, and you're wondering if you've entered some sort of time vortex. Sound familiar...]]></description>
            <content:encoded><![CDATA[Picture this: You sit down at your Mac with a coffee, planning to "quickly check a few emails." Next thing you know, it's 3 PM, your coffee has achieved room temperature, and you're wondering if you've entered some sort of time vortex. Sound familiar?

If you're nodding your head (and possibly rubbing your stiff neck), you're not alone. In our hyper-connected world, time has a sneaky way of slipping through our fingers like sand – or like that last slice of pizza when you're not paying attention.

That's why I built a session tracker that lives right in your Mac's menu bar. It's like having a gentle, persistent friend who reminds you to take breaks, tracks your work patterns, and occasionally judges your life choices (in the nicest possible way).

## What Does This Digital Time Wizard Do?

Our session tracker is basically a sophisticated time-keeping ninja that:

1. **Tracks Your Current Work Session** - It knows when you start working and keeps a running timer
    
2. **Detects Sleep/Wake Cycles** - Smartly figures out when you've been away from your computer
    
3. **Logs Daily Activity** - Keeps a record of all your work sessions throughout the day
    
4. **Sends Gentle Reminders** - Politely suggests you take a break after an hour (because your eyes and back will thank you)
    
5. **Shows Beautiful Stats** - Displays your current session time and daily totals right in your menu bar
    

Think of it as a Fitbit for your productivity, but instead of counting steps, it's counting the minutes you spend glued to your screen.

## The Magic Behind the Curtain

This isn't just any ordinary timer script – it's a surprisingly sophisticated piece of bash wizardry that handles all sorts of edge cases:

### Smart Session Detection

The script doesn't just start counting from when you run it. It's smart enough to:

* Detect when your Mac has been rebooted
    
* Figure out when you've been away (sleeping, lunch break, or that inevitable YouTube rabbit hole)
    
* Resume tracking seamlessly when you return
    

### Intelligent Time Gap Detection

Here's where it gets clever: the script monitors for gaps in activity longer than 2 minutes. If it detects you've been away, it logs your previous session and starts a new one. It's like having a personal assistant who's really good at reading between the lines.

### Cross-Reboot Persistence

Even if you restart your Mac, the script remembers your previous session and can estimate when it ended. It's like having a time-tracking elephant – it never forgets.

## Setting Up Your New Digital Productivity Buddy

Ready to get this bad boy running on your Mac? Here's how to set it up:

### Prerequisites

First, you'll need **xbar** (formerly BitBar), which is a fantastic tool that lets you put the output of any script in your Mac's menu bar:

```bash
# Install xbar using Homebrew
brew install xbar
```

If you don't have Homebrew installed, grab it from [brew.sh](http://brew.sh) first.

### Optional but Recommended: Better Notifications

For prettier notifications, install `terminal-notifier`:

```bash
brew install terminal-notifier
```

Don't worry if you skip this – the script will fall back to macOS's built-in notification system.

### Installing the Script

1. **Create the xbar plugins directory** (if it doesn't exist):
    

```bash
mkdir -p "~/Library/ApplicationSupport/xbar/plugins"
```

2. **Create the script file**:
    

```bash
nano "~/Library/Application Support/xbar/plugins/current-session.1m.sh"
```

3. **Copy and paste the following code**:
    

```bash
#!/bin/bash

# Set PATH to include common locations
export PATH="/usr/local/bin:/opt/homebrew/bin:$PATH"

# File to store the session start time
SESSION_FILE="/tmp/current_session_start"
# File to store daily activity log
DAILY_LOG_FILE="/tmp/daily_activity_$(date +%Y%m%d)"

# Get current time
CURRENT_TIME=$(date +%s)

# Check if system was recently awakened by looking at uptime vs session file age
UPTIME_SECONDS=$(sysctl -n kern.boottime | awk '{print $4}' | sed 's/,//')
BOOT_TIME=$(date -r "$UPTIME_SECONDS" +%s 2>/dev/null || echo "$CURRENT_TIME")

# If session file doesn't exist or is older than boot time, create new session
if [ ! -f "$SESSION_FILE" ]; then
  SESSION_START="$CURRENT_TIME"
  SESSION_FILE_NEEDS_UPDATE=true
else
  STORED_TIME=$(cat "$SESSION_FILE")

  # Check if the stored time is valid (numeric and reasonable)
  if ! [[ "$STORED_TIME" =~ ^[0-9]+$ ]] || [ "$STORED_TIME" -lt 1000000000 ] || [ "$STORED_TIME" -gt $((CURRENT_TIME + 86400)) ]; then
    # Invalid timestamp, start new session
    SESSION_START="$CURRENT_TIME"
    SESSION_FILE_NEEDS_UPDATE=true
  elif [ "$STORED_TIME" -lt "$BOOT_TIME" ]; then
    # Session file is from before last boot, start new session
    SESSION_START="$CURRENT_TIME"
    SESSION_FILE_NEEDS_UPDATE=true
  else
    # Check if we've been asleep (gap in timestamps)
    LAST_CHECK_FILE="/tmp/last_session_check"
    if [ -f "$LAST_CHECK_FILE" ]; then
      LAST_CHECK=$(cat "$LAST_CHECK_FILE")
      TIME_GAP=$((CURRENT_TIME - LAST_CHECK))

      # If gap is more than 2 minutes, assume we were asleep and start new session
      if [ "$TIME_GAP" -gt 120 ]; then
        SESSION_START="$CURRENT_TIME"
        SESSION_FILE_NEEDS_UPDATE=true
      else
        SESSION_START="$STORED_TIME"
        SESSION_FILE_NEEDS_UPDATE=false
      fi
    else
      SESSION_START="$STORED_TIME"
      SESSION_FILE_NEEDS_UPDATE=false
    fi
  fi
fi

# Track daily activity - check if we need to log previous session before updating files
LAST_CHECK_FILE="/tmp/last_session_check"
NEW_SESSION_STARTED=false

# Check if we're about to start a new session and need to log the previous one
if [ -f "$LAST_CHECK_FILE" ] &amp;&amp; [ -f "$SESSION_FILE" ]; then
  LAST_CHECK=$(cat "$LAST_CHECK_FILE")
  PREV_SESSION_START=$(cat "$SESSION_FILE")
  TIME_GAP=$((CURRENT_TIME - LAST_CHECK))

  # If gap detected and we have a valid previous session, log it before starting new session
  if [ "$TIME_GAP" -gt 120 ] &amp;&amp; [ "$PREV_SESSION_START" -lt "$LAST_CHECK" ]; then
    PREV_SESSION_DURATION=$((LAST_CHECK - PREV_SESSION_START))
    # Only log sessions longer than 1 minute
    if [ "$PREV_SESSION_DURATION" -gt 60 ]; then
      echo "$PREV_SESSION_START $LAST_CHECK $PREV_SESSION_DURATION" >> "$DAILY_LOG_FILE"
    fi
    NEW_SESSION_STARTED=true
  fi
elif [ ! -f "$LAST_CHECK_FILE" ] &amp;&amp; [ -f "$SESSION_FILE" ]; then
  # First run after boot - check if we should log a session from before reboot
  PREV_SESSION_START=$(cat "$SESSION_FILE")
  if [ "$PREV_SESSION_START" -lt "$BOOT_TIME" ]; then
    # Session was from before boot, try to estimate when it ended (use boot time)
    PREV_SESSION_DURATION=$((BOOT_TIME - PREV_SESSION_START))
    if [ "$PREV_SESSION_DURATION" -gt 60 ] &amp;&amp; [ "$PREV_SESSION_DURATION" -lt 86400 ]; then
      # Only log if duration seems reasonable (between 1 minute and 24 hours)
      echo "$PREV_SESSION_START $BOOT_TIME $PREV_SESSION_DURATION" >> "$DAILY_LOG_FILE"
    fi
  fi
fi

# Update session file if we're starting a new session
if [ "$SESSION_FILE_NEEDS_UPDATE" = true ]; then
  echo "$SESSION_START" > "$SESSION_FILE"
  # Reset notification tracking for new session
  rm -f "/tmp/last_rest_notification"
fi

# Update the last check time
echo "$CURRENT_TIME" > "/tmp/last_session_check"

# Calculate the session duration in seconds
SESSION_DURATION=$((CURRENT_TIME - SESSION_START))

# Check if we need to show a rest notification (every hour)
NOTIFICATION_FILE="/tmp/last_rest_notification"
if [ "$SESSION_DURATION" -gt 3600 ]; then
  # Check if we've already shown a notification for this hour
  CURRENT_HOUR=$((SESSION_DURATION / 3600))
  if [ -f "$NOTIFICATION_FILE" ]; then
    LAST_NOTIFICATION_HOUR=$(cat "$NOTIFICATION_FILE")
  else
    LAST_NOTIFICATION_HOUR=0
  fi

  # Show notification if we haven't shown one for this hour yet
  if [ "$CURRENT_HOUR" -gt "$LAST_NOTIFICATION_HOUR" ]; then
    # Check if terminal-notifier is available
    if command -v terminal-notifier >/dev/null 2>&amp;1; then
      terminal-notifier -title "Session Tracker" -subtitle "Time for a rest" -message "You've been working for ${CURRENT_HOUR} hour(s). Consider taking a break!" -sound funk -group "session-tracker"
    else
      # Fallback to osascript if terminal-notifier is not available
      osascript -e "display notification \"You've been working for ${CURRENT_HOUR} hour(s). Consider taking a break!\" with title \"Session Tracker\" subtitle \"Time for a rest\" sound name \"Glass\""
    fi
    echo "$CURRENT_HOUR" > "$NOTIFICATION_FILE"
  fi
fi

# Calculate total daily activity
TOTAL_TODAY=0
if [ -f "$DAILY_LOG_FILE" ]; then
  while read -r start_time end_time duration; do
    if [ -n "$duration" ] &amp;&amp; [ "$duration" -gt 0 ]; then
      TOTAL_TODAY=$((TOTAL_TODAY + duration))
    fi
  done < "$DAILY_LOG_FILE"
fi

# Add current session to today's total
TOTAL_TODAY=$((TOTAL_TODAY + SESSION_DURATION))

# Convert duration to human-readable format
DURATION_HOURS=$((SESSION_DURATION / 3600))
DURATION_MINUTES=$(( (SESSION_DURATION % 3600) / 60 ))
DURATION_SECONDS=$((SESSION_DURATION % 60))

# Convert total daily time to human-readable format
TOTAL_HOURS=$((TOTAL_TODAY / 3600))
TOTAL_MINUTES=$(( (TOTAL_TODAY % 3600) / 60 ))

# Format the session output
if [ $DURATION_HOURS -gt 0 ]; then
  DURATION_STRING="${DURATION_HOURS}h ${DURATION_MINUTES}m"
else
  DURATION_STRING="${DURATION_MINUTES}m"
fi

# Format the daily total output
if [ $TOTAL_HOURS -gt 0 ]; then
  TOTAL_STRING="${TOTAL_HOURS}h ${TOTAL_MINUTES}m"
else
  TOTAL_STRING="${TOTAL_MINUTES}m"
fi

# Output the session duration and daily total
# Add warning indicator if session is over 1 hour
if [ "$SESSION_DURATION" -gt 3600 ]; then
  echo "⚠️ $DURATION_STRING | size=10"
else
  echo "🕒 $DURATION_STRING | size=10"
fi
echo "---"
echo "📊 Current Session: $DURATION_STRING"
echo "📅 Today Total: $TOTAL_STRING"

# Show session breakdown if there are previous sessions
if [ -f "$DAILY_LOG_FILE" ] &amp;&amp; [ -s "$DAILY_LOG_FILE" ]; then
  SESSION_COUNT=$(wc -l < "$DAILY_LOG_FILE")
  echo "🔢 Sessions Today: $((SESSION_COUNT + 1))"
fi

# Add rest reminder in dropdown if session is over 1 hour
if [ "$SESSION_DURATION" -gt 3600 ]; then
  echo "---"
  echo "⏰ Take a break! You've been working for over an hour | color=orange"
fi
```

4. **Make it executable**:
    

```bash
chmod +x "~/Library/Application Support/xbar/plugins/current-session.1m.sh"
```

5. **Start xbar and refresh**:
    
    * Launch xbar from your Applications folder
        
    * Click the xbar icon in your menu bar and select "Refresh all"
        

## Understanding the Code: A Gentle Journey Through Bash Land

Let's break down what this script does, step by step:

### The Setup Phase

```bash
SESSION_FILE="/tmp/current_session_start"
DAILY_LOG_FILE="/tmp/daily_activity_$(date +%Y%m%d)"
```

We store our session data in temporary files. The daily log file includes the date, so each day gets its own log file. It's like having a new diary page for each day!

### The Detective Work

```bash
UPTIME_SECONDS=$(sysctl -n kern.boottime | awk '{print $4}' | sed 's/,//')
BOOT_TIME=$(date -r "$UPTIME_SECONDS" +%s 2>/dev/null || echo "$CURRENT_TIME")
```

This is where we get all CSI about when your Mac was last booted. We use this to figure out if your session file is from before a reboot.

### The Smart Session Logic

The script has several scenarios it handles:

1. **First run ever**: Creates a new session
    
2. **File exists but invalid**: Starts fresh (protects against corrupted data)
    
3. **File is from before reboot**: Starts a new session post-reboot
    
4. **Checking for sleep gaps**: If there's a gap &gt; 2 minutes, it assumes you were away
    

### The Notification System

```bash
if [ "$SESSION_DURATION" -gt 3600 ]; then
  # Time for a break notification logic
fi
```

After an hour of work, the script gently reminds you to take a break. It's like having a caring friend who's also really good at math.

## What You'll See in Action

Once everything is running, you'll see:

* **🕒 45m** in your menu bar (showing your current session time)
    
* **⚠️ 1h 23m** when you've been working for over an hour (subtle hint to take a break)
    
* A dropdown menu showing:
    
    * 📊 Current Session: 1h 23m
        
    * 📅 Today Total: 3h 45m
        
    * 🔢 Sessions Today: 4
        
    * ⏰ Take a break! reminder (if you've been working too long)
        

## The Beauty of Simplicity

What I love about this solution is that it's:

* **Lightweight**: Uses minimal system resources
    
* **Unobtrusive**: Sits quietly in your menu bar
    
* **Smart**: Handles edge cases you didn't even think about
    
* **Customizable**: Easy to modify for your specific needs
    

## Customization Ideas

Want to make it your own? Here are some ideas:

1. **Change the break reminder interval**: Modify the `3600` seconds (1 hour) to whatever works for you
    
2. **Add different notification sounds**: Change the `sound funk` to `sound glass`, `sound ping`, etc.
    
3. **Modify the time gap detection**: Change the `120` seconds gap threshold
    
4. **Add more detailed logging**: Include timestamps, session names, or project tags
    

## The Philosophical Side: Why This Matters

In our always-on world, this simple script serves a deeper purpose. It's not just about tracking time – it's about being mindful of how we spend our most precious resource. By making our work patterns visible, we can:

* **Recognize unhealthy patterns** (like those 4-hour coding binges)
    
* **Celebrate productivity** (look at all those completed sessions!)
    
* **Build better habits** (those break reminders really do help)
    
* **Understand our rhythms** (maybe you're most productive in the morning?)
    

## Wrapping Up

Building this session tracker was a fun exercise in bash scripting and practical problem-solving. It started as a simple "how long have I been working?" question and evolved into a surprisingly sophisticated time-tracking system.

The best part? It's taught me to actually take breaks. Turns out, when your computer politely suggests you step away from the screen, you're more likely to listen than when your back is screaming at you.

So go ahead, give it a try! Your future self (and your neck) will thank you. And who knows? You might just discover that you're either more or less productive than you thought. Either way, at least you'll know for sure.]]></content:encoded>
            <category>Productivity</category>
            <category>automation</category>
            <category>Bash</category>
            <category>Time management</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1752685531619/cf9cd6f6-c307-40c0-94cf-b278297d2689.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Building a Real-Time CPU Monitor for macOS with xbar]]></title>
            <link>https://www.ravgeet.in/blog/building-a-real-time-cpu-monitor-for-macos-with-xbar</link>
            <guid>https://www.ravgeet.in/blog/building-a-real-time-cpu-monitor-for-macos-with-xbar</guid>
            <pubDate>Fri, 04 Jul 2025 08:47:47 GMT</pubDate>
            <description><![CDATA[Have you ever noticed your Mac's fan spinning wildly but couldn't quickly identify which process was consuming all your CPU?
As a developer who uses Visual Studio Code daily, I rely heavily on its rich ecosystem of extensions to boost my productivity...]]></description>
            <content:encoded><![CDATA[Have you ever noticed your Mac's fan spinning wildly but couldn't quickly identify which process was consuming all your CPU?

As a developer who uses Visual Studio Code daily, I rely heavily on its rich ecosystem of extensions to boost my productivity. But over time, I started noticing my MacBook Air heating up, the fans spinning loudly, and my system becoming sluggish — all while I was just editing code. When I opened the Activity Monitor, I saw one or more mysterious "Code Helper (Plugin)" processes consuming 90–100% CPU, but there was no clear indication of which extension was responsible.

VS Code spawns multiple helper processes, and most of them are generically named, making it incredibly difficult to trace high CPU usage back to a specific extension. This left me guessing — was it Copilot? ESLint? Live Server? I needed a way to monitor these extensions intelligently, without sacrificing performance or productivity.

That's what led me to build a solution — a lightweight tool that monitors CPU usage in real time, maps it to the responsible extension, and alerts me before things spiral out of control. Today, I'll walk you through building a lightweight, real-time CPU monitoring tool that lives in your macOS menu bar and sends notifications when processes exceed your defined thresholds.

## What We're Building

Our CPU monitor will:

* Display CPU status directly in the menu bar
    
* Alert you when any process exceeds 80% CPU usage
    
* Send native macOS notifications for high CPU processes
    
* Provide special handling for VS Code extensions and helpers
    
* Show detailed process information (PID, name, command)
    
* Indicate when all processes are running normally
    

## Prerequisites

Before we start, you'll need:

* macOS (this guide is macOS-specific)
    
* [xbar](https://xbarapp.com/) installed (formerly BitBar)
    
* Basic familiarity with shell scripting
    

## The Architecture

Our solution uses a simple but effective approach:

1. **xbar Integration**: The script runs every 5 minutes (indicated by the `.5m.` in the filename)
    
2. **Process Monitoring**: We use `ps` to capture all running processes with their CPU usage
    
3. **Threshold Detection**: Any process using more than 80% CPU triggers an alert
    
4. **Native Notifications**: We leverage macOS's `osascript` for system notifications
    
5. **Fallback Support**: Includes support for `terminal-notifier` as a backup
    

## The Complete Script

Here's the full [`vscode-ext-monitor.5m.sh`](http://vscode-ext-monitor.5m.sh) script:

```bash
#!/bin/bash

CPU_THRESHOLD=80.0
HIGH_CPU_FOUND=0

# Menu Bar Title
echo "🖥️ CPU Monitor"

# Store process information in a temporary file to avoid subshell issues
TEMP_FILE=$(mktemp)
ps -Ao pid,%cpu,command | grep -v "ps -Ao" | grep -v grep > "$TEMP_FILE"

# Read from the temporary file
while IFS= read -r line; do
  if [ -z "$line" ]; then
    continue
  fi

  cpu=$(echo "$line" | awk '{print $2}')
  pid=$(echo "$line" | awk '{print $1}')
  command=$(echo "$line" | cut -d ' ' -f3-)

  # Skip if CPU is not a valid number or is 0.0
  if ! echo "$cpu" | grep -q '^[0-9]*\.[0-9]*$' || [ "$cpu" = "0.0" ]; then
    continue
  fi

  is_high=$(echo "$cpu > $CPU_THRESHOLD" | bc)

  if [ "$is_high" -eq 1 ]; then
    HIGH_CPU_FOUND=1
    echo "---"
    echo "⚠️ High CPU ($cpu%)"
    echo "PID: $pid"

    # Get process name from command
    process_name=$(echo "$command" | awk '{print $1}' | xargs basename 2>/dev/null || echo "Unknown")
    echo "📱 Process: $process_name"

    # Show truncated command
    echo "💻 ${command:0:60}..."

    notification_title="From CPU Monitor"

    # Special handling for different process types
    if echo "$command" | grep -q ".vscode/extensions"; then
      ext=$(echo "$command" | grep -o "/Users/[^ ]*\.vscode/extensions/[^ ]*")
      echo "🧩 VS Code Extension: $(basename "$ext")"
      notification_message="High CPU: ${cpu}% by VS Code extension $(basename "$ext")"
    elif echo "$command" | grep -q "Code Helper"; then
      echo "🔧 VS Code Helper Process"
      notification_message="High CPU: ${cpu}% by VS Code Helper"
    else
      notification_message="High CPU: ${cpu}% by $process_name"
    fi

    # Desktop notification (macOS only) - with error handling
    if command -v osascript >/dev/null 2>&amp;1; then
      osascript -e "display notification \"$notification_message\" with title \"$notification_title\"" 2>/dev/null || {
        # Fallback: try using terminal-notifier if available
        if command -v terminal-notifier >/dev/null 2>&amp;1; then
          terminal-notifier -title "$notification_title" -message "$notification_message" 2>/dev/null
        fi
      }
    fi
  fi
done < "$TEMP_FILE"

# Clean up temporary file
rm -f "$TEMP_FILE"

if [ "$HIGH_CPU_FOUND" -eq 0 ]; then
  echo "---"
  echo "✅ All processes under ${CPU_THRESHOLD}%"
fi
```

## Key Technical Decisions

### 1\. Avoiding Subshell Issues

Initially, we faced a common bash pitfall where notifications wouldn't work because the `while` loop was running in a subshell:

```bash
# This doesn't work for GUI operations
ps ... | while read line; do
  osascript -e "display notification ..."
done
```

**Solution**: We use a temporary file approach to avoid the subshell:

```bash
TEMP_FILE=$(mktemp)
ps -Ao pid,%cpu,command > "$TEMP_FILE"
while read line; do
  # Process data and send notifications
done < "$TEMP_FILE"
rm -f "$TEMP_FILE"
```

### 2\. Robust CPU Validation

We validate CPU values to ensure we're working with actual numeric data:

```bash
if ! echo "$cpu" | grep -q '^[0-9]*\.[0-9]*$' || [ "$cpu" = "0.0" ]; then
  continue
fi
```

This prevents errors from malformed process data.

### 3\. Smart Process Classification

The script intelligently categorizes processes:

* **VS Code Extensions**: Detected by `.vscode/extensions` in the command path
    
* **VS Code Helpers**: Identified by "Code Helper" in the command
    
* **General Processes**: Everything else gets generic handling
    

### 4\. Notification Reliability

We implement a two-tier notification system:

```bash
osascript -e "display notification ..." 2>/dev/null || {
  # Fallback to terminal-notifier if available
  if command -v terminal-notifier >/dev/null 2>&amp;1; then
    terminal-notifier -title "..." -message "..."
  fi
}
```

## Installation and Setup

1. **Install xbar** if you haven't already:
    
    ```bash
    brew install --cask xbar
    ```
    
2. **Create the plugin directory**:
    
    ```bash
    mkdir -p "$HOME/Library/Application Support/xbar/plugins"
    ```
    
3. **Save the script** as [`vscode-ext-monitor.5m.sh`](http://vscode-ext-monitor.5m.sh) in the plugins directory
    
4. **Make it executable**:
    
    ```bash
    chmod +x "$HOME/Library/Application Support/xbar/plugins/vscode-ext-monitor.5m.sh"
    ```
    
5. **Launch xbar** and refresh to see your new CPU monitor
    

## Customization Options

### Adjust the CPU Threshold

Change the threshold by modifying this line:

```bash
CPU_THRESHOLD=80.0  # Change to your preferred percentage
```

### Modify the Update Frequency

Rename the file to change how often it runs:

* `.`[`1m.sh`](http://1m.sh) = Every minute
    
* `.`[`30s.sh`](http://30s.sh) = Every 30 seconds
    
* `.`[`10m.sh`](http://10m.sh) = Every 10 minutes
    

### Add More Process Types

Extend the classification logic:

```bash
elif echo "$command" | grep -q "chrome"; then
  echo "🌐 Chrome Process"
  notification_message="High CPU: ${cpu}% by Chrome"
```

## Troubleshooting

### Notifications Not Appearing?

1. **Check System Preferences**: Ensure notifications are enabled for Terminal in System Preferences &gt; Notifications &amp; Focus
    
2. **Test manually**:
    
    ```bash
    osascript -e 'display notification "Test" with title "Test"'
    ```
    
3. **Install terminal-notifier as backup**:
    
    ```bash
    brew install terminal-notifier
    ```
    

### Script Not Running?

1. **Verify file permissions**:
    
    ```bash
    ls -la "$HOME/Library/Application Support/xbar/plugins/"
    ```
    
2. **Check xbar is running** and refresh the menu
    
3. **Test the script manually**:
    
    ```bash
    cd "$HOME/Library/Application Support/xbar/plugins/"
    ./vscode-ext-monitor.5m.sh
    ```
    

## What's Next?

This CPU monitor provides a solid foundation that you can extend further:

* **Memory Monitoring**: Add RAM usage alerts
    
* **Network Activity**: Monitor processes with high network usage
    
* **Historical Tracking**: Log high CPU events to a file
    
* **Kill Process Feature**: Add menu options to terminate problematic processes
    
* **Custom Thresholds**: Different thresholds for different process types
    

## Conclusion

Building system monitoring tools doesn't require complex frameworks or heavy applications. With a simple bash script and xbar, we've created a lightweight, effective CPU monitor that:

* Provides real-time visibility into system performance
    
* Sends proactive notifications before problems escalate
    
* Offers detailed process information for quick troubleshooting
    
* Runs efficiently with minimal system overhead
    

The beauty of this approach is its simplicity and customizability. You have full control over the monitoring logic, notification behavior, and display format. Plus, since it's just a bash script, you can easily modify it to suit your specific needs.

*This CPU monitor has been tested on macOS Sequoia and later. The script should work on earlier versions but may require minor adjustments for notification handling.*]]></content:encoded>
            <category>Productivity</category>
            <category>General Programming</category>
            <category>macOS</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1751618719871/6e4b910d-06b5-4166-bf21-acd18e159db7.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Automate GitHub stats reporting with scheduled pipelines]]></title>
            <link>https://www.ravgeet.in/blog/automate-github-stats-reporting-with-scheduled-pipelines</link>
            <guid>https://www.ravgeet.in/blog/automate-github-stats-reporting-with-scheduled-pipelines</guid>
            <pubDate>Fri, 04 Jul 2025 06:30:10 GMT</pubDate>
            <description><![CDATA[Release notes provide essential documentation when a new software version is released. For release notes to be most effective, dev teams must consolidate all of the work that has been done since the previous release. It is a hectic task that requires...]]></description>
            <content:encoded><![CDATA[Release notes provide essential documentation when a new software version is released. For release notes to be most effective, dev teams must consolidate all of the work that has been done since the previous release. It is a hectic task that requires a lot of effort and time sorting through weeks or even months of software issues and pull requests.

Why not make the life of the release team easier by automating the creation of release notes? You can, using a combination of GitHub API and a [CI/CD tool](https://circleci.com/blog/what-is-a-ci-cd-pipeline/) like CircleCI. Automate the task of fetching issues and pull requests, and put them in a single place where they can be accessed easily by the release notes team.

In this tutorial, you’ll learn to use the GitHub API and CircleCI to create weekly stats for your GitHub repositories. The plan is to build an automated workflow using CircleCI [scheduled pipelines](https://circleci.com/blog/using-scheduled-pipelines/). The pipeline will fetch all the issues and pull requests made during a specified interval, save these stats in a file, and commit this file back to the repository.

Read the full blog on [CircleCI](https://circleci.com/blog/automate-github-stats/).

%[https://circleci.com/blog/automate-github-stats/] 

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come across while surfing the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [Github](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>General Programming</category>
            <category>JavaScript</category>
            <category>Web Development</category>
            <category>automation</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1750427972230/9fcac32e-9825-4651-ada6-784f5ba017f8.avif" length="0" type="image/avif"/>
        </item>
        <item>
            <title><![CDATA[Getting the Most Out of GitHub Copilot Chat in VS Code]]></title>
            <link>https://www.ravgeet.in/blog/getting-the-most-out-of-github-copilot-chat-in-vs-code</link>
            <guid>https://www.ravgeet.in/blog/getting-the-most-out-of-github-copilot-chat-in-vs-code</guid>
            <pubDate>Mon, 30 Jun 2025 18:30:30 GMT</pubDate>
            <description><![CDATA[GitHub Copilot is already an incredible tool for autocompleting code, but if you haven’t tried Copilot Chat, you’re missing out on one of the most powerful AI developer workflows available today.
In this post, you’ll walk through:

How to use Copilot...]]></description>
            <content:encoded><![CDATA[GitHub Copilot is already an incredible tool for autocompleting code, but if you haven’t tried **Copilot Chat**, you’re missing out on one of the most powerful AI developer workflows available today.

In this post, you’ll walk through:

* How to use Copilot Chat in VS Code
    
* What it’s best at
    
* Real-world prompts you can use right away
    

## What Is GitHub Copilot Chat?

Copilot Chat brings the power of ChatGPT **directly into VS Code**, allowing you to ask natural language questions about your code, request explanations, generate tests, fix bugs, refactor logic, and more — without ever leaving your editor.

It’s like having an AI pair programmer that:

* Understands your code context
    
* Works inline or in a chat panel
    
* Helps you learn, debug, and ship faster
    

## What Can You Use Copilot Chat For?

Here are some powerful use cases:

### Code Understanding &amp; Explanation

You can highlight any block of code and ask:

* “Explain what this code does”
    
* “What is the time complexity here?”
    
* “Why am I getting this TypeError?”
    

### Code Improvement &amp; Refactoring

Ask it to:

* “Refactor this function to be cleaner”
    
* “Optimize this loop”
    
* “Suggest better variable names”
    

### Test Generation

Save hours writing boilerplate with prompts like:

* “Write unit tests for this function using Jest”
    
* “Generate test cases for edge inputs”
    

### Code Conversion &amp; Migration

Let it help with transitions like:

* “Convert this JS code to TypeScript”
    
* “Rewrite this to use async/await”
    
* “Switch from useEffect to React Query”
    

### General Cleanup

Ask it to:

* “Remove unused imports”
    
* “Simplify this logic”
    
* “Make this more readable”
    

## Some Example Prompts You Can Use Right Now

| Use Case | Example Prompt |
| --- | --- |
| Understand Code | `Explain this function step by step` |
| Improve Performance | `Optimize this loop for large datasets` |
| Refactor Logic | `Make this code more readable and concise` |
| Convert Language | `Translate this from Python to JavaScript` |
| Debug Errors | `Fix the error in this fetch call` |
| Generate Tests | `Write unit tests for this React component` |
| Learn Concepts | `What is useMemo in React and when to use it?` |
| Clean Up Code | `Remove all unused variables from this file` |

> ✅ Tip: You can highlight a code block and then open Copilot Chat to get smarter, context-aware answers.

## How to Enable Copilot Chat in VS Code

1. Install the GitHub Copilot Chat extension from the [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot-chat)
    
2. Make sure you’re signed in to GitHub with an active Copilot plan
    
3. Open the Copilot Chat panel or use `Cmd/Ctrl + I` to start inline chat
    
4. Highlight code and right-click → **Ask Copilot**
    

## Final Thoughts

Copilot Chat takes the promise of AI-assisted development to the next level. It’s not just about writing code faster — it’s about **thinking through problems, debugging more efficiently, and learning as you go**, all inside your editor.]]></content:encoded>
            <category>General Programming</category>
            <category>Productivity</category>
            <category>Developer</category>
            <category>AI</category>
            <category>copilot</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/nbZHM2uwkJs/upload/9c2c6c473ad9dfe9cc28dc9271647775.jpeg" length="0" type="image/jpeg"/>
        </item>
        <item>
            <title><![CDATA[Generating dynamic sales quotes with Dropbox Sign]]></title>
            <link>https://www.ravgeet.in/blog/generating-dynamic-sales-quotes-with-dropbox-sign</link>
            <guid>https://www.ravgeet.in/blog/generating-dynamic-sales-quotes-with-dropbox-sign</guid>
            <pubDate>Thu, 26 Jun 2025 06:30:33 GMT</pubDate>
            <description><![CDATA[Creating and sending price quotes is a necessary part of business, but it can also be a laborious process, especially if the sales team sends out multiple quotes daily. You can make things easier on yourself and your sales team by automating your sal...]]></description>
            <content:encoded><![CDATA[Creating and sending price quotes is a necessary part of business, but it can also be a laborious process, especially if the sales team sends out multiple quotes daily. You can make things easier on yourself and your sales team by automating your sales quote generation. An excellent way to do that is by using the [**Dropbox Sign API**.](https://sign.dropbox.com/developers)

[Dropbox Sign](https://sign.dropbox.com/developers) allows you to create and send bulk templates, as well as generate dynamic documents such as sales quotes. In this tutorial, you’ll create a command-line utility for generating sales quote documents using the Dropbox Sign API. You’ll pass input data as command line arguments, creating emailed sales quote documents that customers can sign digitally.

Read the full blog on [Dropbox](https://sign.dropbox.com/blog/generating-dynamic-sales-quotes).

%[https://sign.dropbox.com/blog/generating-dynamic-sales-quotes] 

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come across while surfing the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [Github](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>Web Development</category>
            <category>JavaScript</category>
            <category>General Programming</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1750428177112/383145d9-50e5-4cfd-9f1b-1c031a4117e6.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[Improve Table Speed in React by Using Web Workers for Filters]]></title>
            <link>https://www.ravgeet.in/blog/improve-table-speed-in-react-by-using-web-workers-for-filters</link>
            <guid>https://www.ravgeet.in/blog/improve-table-speed-in-react-by-using-web-workers-for-filters</guid>
            <pubDate>Thu, 19 Jun 2025 15:33:56 GMT</pubDate>
            <description><![CDATA[TL;DR: I implemented a Web Worker–powered filtering system in a React data table component to eliminate UI lag and improve responsiveness when working with large datasets. Here's how and why.

The Problem: Filtering Slows the UI
Our application relie...]]></description>
            <content:encoded><![CDATA[> TL;DR: I implemented a **Web Worker–powered filtering system** in a React data table component to eliminate UI lag and improve responsiveness when working with large datasets. Here's how and why.

## The Problem: Filtering Slows the UI

Our application relies heavily on a custom `<NewTable />` component that supports:

* Nested (hierarchical) rows
    
* Column-based filtering
    
* Custom filters
    
* Server-side pagination
    

As datasets grew into the thousands of rows, filtering became noticeably sluggish. The culprit? All filtering logic ran on the **main UI thread**, blocking React’s render cycle and causing the interface to freeze temporarily.

## Goal

Move heavy data-filtering logic off the main thread using **Web Workers**, without breaking existing functionality or developer experience.

## The Solution: Asynchronous Filtering with Web Workers

We built a pipeline that:

1. **Serializes hierarchical data** into a flat structure
    
2. Sends that data to a **dedicated Web Worker**
    
3. Worker filters based on column &amp; custom filters
    
4. Sends back the result
    
5. Updates the UI reactively and shows a loader while waiting
    

## Implementation Breakdown

### 1\. Set up `search.worker.js`

We created a Web Worker dynamically using a blob:

```ts
const searchWorker = () => {
  onmessage = async (e) => {
    importScripts("https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js");
    importScripts("https://cdnjs.cloudflare.com/ajax/libs/dayjs/1.10.7/dayjs.min.js");

    // ... logic for deep filtering, date handling, and custom filters ...
    
    postMessage({ success, result });
  };
};

let code = searchWorker.toString();
code = code.substring(code.indexOf("{") + 1, code.lastIndexOf("}"));
const blob = new Blob([code], { type: "application/javascript" });
const workerScript = URL.createObjectURL(blob);

export default workerScript;
```

### 2\. New Utility Function

A new async utility offloads data filtering:

```ts
const asyncFilterDataWithColumnAndCustomFilters = async (
  data,
  columns,
  columnFilters,
  customFilters,
  setData,
  setIsSearching,
) => {
  const searchWorker = new window.Worker(searchWorkerScript);

  const convertedData = data.map(row => ({
    id: row.id,
    values: columns.map(col => col.selector?.(row) ?? row[col.id]),
    items: (row.items || []).map(item => ({
      id: item.id,
      values: columns.map(col => col.selector?.(item) ?? item[col.id]),
    })),
  }));

  searchWorker.postMessage({ data, convertedData, columns, columnFilters, customFilters });

  searchWorker.onmessage = (e) => {
    setData(e.data.result);
    setIsSearching(false);
    searchWorker.terminate();
  };

  searchWorker.onerror = (err) => {
    console.error("Worker error", err.message);
    setIsSearching(false);
    searchWorker.terminate();
  };
};
```

### 3\. Hook Update

We extended the existing table filter hook:

```ts
export const useFilterDataWithColumnAndCustomFilters = ({
  data,
  columns,
  columnFilters,
  customFilters,
  setIsSearching,
}) => {
  const [filteredData, setFilteredData] = useState({ data: [], diff: 0 });

  useEffect(() => {
    asyncFilterDataWithColumnAndCustomFilters(
      data,
      columns,
      columnFilters,
      customFilters,
      setIsSearching,
      setFilteredData
    );
  }, [data, columnFilters, customFilters]);

  return { filteredData: filteredData.data, diff: filteredData.diff };
};
```

### 4\. Loading Indicator

We updated the table’s loading prop:

```tsx
<NewTable
  isLoading={isLoading || isSearching}
  // ...
/>
```

## Bonus: What the Worker Can Handle

* `BOOLEAN`, `DATE`, `DATETIME`, and `NUMBER` types
    
* Interval filters (e.g. date ranges)
    
* Null checks: `eq: "null"` and `ne: "null"`
    
* Multi-select picklists
    
* Filters nested rows **and** their parents
    

## Benefits

* **UI never freezes** during filtering
    
* **Filters run faster**, even on large datasets
    
* **Code is modular** and easy to maintain
    
* **Great user experience** with real-time filtering feedback
    

## Key Takeaways

* Use **Web Workers** to offload expensive tasks in the browser.
    
* Normalize complex data structures before sending to the worker.
    
* Gracefully handle worker errors and show meaningful UI states.
    
* It's surprisingly easy to integrate with React.
    

## Final Thoughts

Offloading filtering to a Web Worker has been one of the most impactful performance wins for our frontend in recent times. If you're working with large tables or slow filters, **give workers a shot** — your users (and frame rate) will thank you.

**Want help integrating something similar into your React app?**  
Feel free to [connect with me](https://ravgeet.in/contact).

*I wrote this blog post for my company,* [*CloudAnswers*](https://cloudanswers.com/)*.*]]></content:encoded>
            <category>General Programming</category>
            <category>Web Development</category>
            <category>performance</category>
            <category>React</category>
            <category>JavaScript</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/yaK5It0P0Gc/upload/483ed162f5c50831e986593bdbaed484.jpeg" length="0" type="image/jpeg"/>
        </item>
        <item>
            <title><![CDATA[How I Use GitHub Copilot and ChatGPT Together as a Frontend Developer]]></title>
            <link>https://www.ravgeet.in/blog/how-i-use-github-copilot-and-chatgpt-together-as-a-frontend-developer</link>
            <guid>https://www.ravgeet.in/blog/how-i-use-github-copilot-and-chatgpt-together-as-a-frontend-developer</guid>
            <pubDate>Mon, 16 Jun 2025 08:09:14 GMT</pubDate>
            <description><![CDATA[As a frontend developer, I'm constantly juggling between writing clean code, shipping features fast, and keeping my sanity intact. AI tools like GitHub Copilot and ChatGPT have completely changed how I work. While each tool is powerful on its own, us...]]></description>
            <content:encoded><![CDATA[As a frontend developer, I'm constantly juggling between writing clean code, shipping features fast, and keeping my sanity intact. AI tools like **GitHub Copilot** and **ChatGPT** have completely changed how I work. While each tool is powerful on its own, using them **together** has helped me work faster, think clearly, and code smartly.

Here’s how I personally use **Copilot + ChatGPT** in my daily workflow.

---

## TL;DR: My Quick Comparison

| Tool | What I Use It For |
| --- | --- |
| **GitHub Copilot** | Real-time code completion inside my IDE (VS Code) |
| **ChatGPT** | Explaining bugs, brainstorming UI, writing docs, or generating code blocks |

I see Copilot as my **coding sidekick inside the editor**, and ChatGPT as my **thinking partner outside of it**.

---

## Real Workflow: Building a React Component

### Step 1: I Ask ChatGPT to Help Plan It

When I need something like a responsive navbar with Tailwind CSS, I type:

> "Create a responsive navbar with logo, links, and hamburger menu using React and Tailwind."

ChatGPT usually gives me a great starting point with code, accessibility notes, and even file structure suggestions.

### Step 2: I Paste That Into VS Code and Let Copilot Take Over

As I begin typing:

```js
function Navbar() {
  return (
```

Copilot fills in:

```js
<nav className="bg-white shadow-md p-4 flex justify-between items-center">
  <div className="text-xl font-bold">Logo</div>
  ...
</nav>
```

It handles the obvious stuff—repetitive patterns, responsive classes, even conditional rendering—while I focus on logic.

## How I Use Them Together Every Day

### 1\. **Rapid Prototyping**

* ChatGPT helps me quickly draft UI layouts.
    
* Copilot finishes JSX, props, and common logic on the fly.
    

### 2\. **Debugging**

* When I hit an error, I paste it into ChatGPT and ask:
    
    > "What’s wrong with this error?"
    
* Then I fix the bug in VS Code with Copilot suggesting inline changes.
    

### 3\. **Writing Tests**

* I ask ChatGPT to generate tests for my React components.
    
* I then use Copilot to autocomplete the repetitive test setup or assertions.
    

### 4\. **Refactoring**

* I send messy functions to ChatGPT to break them down and improve naming.
    
* Back in VS Code, Copilot speeds up implementing the improved version.
    

### 5\. **Explaining My Code**

* When I want to document my code or explain it to my team, I ask ChatGPT:
    
    > "Explain this hook and why the useEffect dependency array matters."
    

## My Favorite Productivity Tips

### Tip 1: Comment-Driven Prompts for Copilot

```js
// Create a responsive card component with image and title
```

Copilot often gives me a complete JSX block instantly.

### Tip 2: ChatGPT for Documentation

I paste a tricky function into ChatGPT and say:

> "Write a JSDoc for this and suggest better variable names."

### Tip 3: Code Reviews Get Easier

I now use GitHub Copilot to explain my PRs or generate commit messages. Copilot helps me fix minor issues before I even push.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1750060967446/43b291bb-9d50-484b-9c46-cfc346eb6f86.png align="center")

## Bonus Tools I Like

| Tool | Why I Use It |
| --- | --- |
| **VS Code + Copilot** | For live code suggestions |
| **ChatGPT Web + API** | For deeper analysis, ideas, and code generation |
| **ChatGPT Plugins** | For testing or GitHub integrations |

## My Final Thoughts

Using both GitHub Copilot and ChatGPT hasn’t just saved me time—it’s **leveled up how I think and build**.

Copilot gives me instant coding speed in the editor, while ChatGPT helps me think clearly, plan ahead, and document better. Whether I’m building a component, squashing bugs, or writing docs, these two tools make the process smoother.

So if you're a frontend dev like me, give this duo a try. You might never want to code alone again. ✨]]></content:encoded>
            <category>webdev</category>
            <category>github copilot</category>
            <category>chatgpt</category>
            <category>React</category>
            <category>Productivity</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/kjqTlMHLci4/upload/60cb77f5390dac04a3a7d5c20b8ec41d.jpeg" length="0" type="image/jpeg"/>
        </item>
        <item>
            <title><![CDATA[How to Build an Internal Company Wiki from Scratch]]></title>
            <link>https://www.ravgeet.in/blog/how-to-build-an-internal-company-wiki-from-scratch</link>
            <guid>https://www.ravgeet.in/blog/how-to-build-an-internal-company-wiki-from-scratch</guid>
            <pubDate>Thu, 19 Jan 2023 07:39:29 GMT</pubDate>
            <description><![CDATA[A company wiki is a knowledge hub where organization-specific information can be easily accessed by the individuals working in an organization. Information in the hub can be related to engineering operations, hiring procedures, employee information, ...]]></description>
            <content:encoded><![CDATA[A company wiki is a knowledge hub where organization-specific information can be easily accessed by the individuals working in an organization. Information in the hub can be related to engineering operations, hiring procedures, employee information, and other company-specific information. Creating and maintaining a company wiki can be complex, but tools like GraphQL and Hygraph (previously GraphCMS) can make it easier.

[**GraphQL**](https://graphql.org/) is a query language for APIs that allows you to request and fetch only the data that you want.

[**Hygraph**](https://hygraph.com/) is a cloud platform for creating databases, tables, and powerful GraphQL APIs. It allows you to create content on the fly with features such as text editors, workflows, and advanced roles.

In this tutorial, you’ll learn to create an internal company wiki from scratch. The backend and content management will be implemented with [**Hygraph**](https://hygraph.com/), and the frontend with [**Next.js**](https://nextjs.org/).

Read the full blog on [Hygraph](https://hygraph.com/blog/build-company-wiki).

%[https://hygraph.com/blog/build-company-wiki] 

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come across while surfing the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [GitHub](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>General Programming</category>
            <category>cms</category>
            <category>Frontend Development</category>
            <category>Next</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1674113713549/310d2fab-ad57-43b0-8b8d-748028208bee.jpeg" length="0" type="image/jpeg"/>
        </item>
        <item>
            <title><![CDATA[Frontend Monitoring: A Complete Guide]]></title>
            <link>https://www.ravgeet.in/blog/frontend-monitoring-a-complete-guide</link>
            <guid>https://www.ravgeet.in/blog/frontend-monitoring-a-complete-guide</guid>
            <pubDate>Fri, 02 Dec 2022 12:20:26 GMT</pubDate>
            <description><![CDATA[Frontend monitoring is a group of techniques for measuring application layer performance, accessibility, uptime, and error tracking and can also be used in web analytics. In other words, these methods monitor a software application’s frontend, the la...]]></description>
            <content:encoded><![CDATA[Frontend monitoring is a group of techniques for measuring application layer performance, accessibility, uptime, and error tracking and can also be used in web analytics. In other words, these methods monitor a software application’s frontend, the layer through which a user interacts with the system’s backend.

In this article, you’ll learn about the different aspects of frontend monitoring and related tools that you can use in your own software applications. This guide is suited for developers who want to implement frontend monitoring tools in their applications.

Read the full blog on [Cronitor](https://cronitor.io/blog/frontend-monitoring).

%[https://cronitor.io/blog/frontend-monitoring]

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come across while surfing the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [GitHub](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>Frontend Development</category>
            <category>Web Development</category>
            <category>Programming Blogs</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1725104885444/ce3a37a8-bafd-46cf-b8c1-2bb6af1ae1d4.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Build a Task Assignment App with Twilio Whatsapp, Strapi, and Next.js]]></title>
            <link>https://www.ravgeet.in/blog/build-a-task-assignment-app-with-twilio-whatsapp-strapi-and-nextjs</link>
            <guid>https://www.ravgeet.in/blog/build-a-task-assignment-app-with-twilio-whatsapp-strapi-and-nextjs</guid>
            <pubDate>Wed, 23 Nov 2022 04:43:54 GMT</pubDate>
            <description><![CDATA[In a working environment, each and every individual is assigned a task. Task assignment is one of the most important aspects in the successful completion of a project. However, it is also very important to communicate the tasks assignment duties to t...]]></description>
            <content:encoded><![CDATA[In a working environment, each and every individual is assigned a task. Task assignment is one of the most important aspects in the successful completion of a project. However, it is also very important to communicate the tasks assignment duties to the concerned person. Hence, you need a way to send a message to the assignee that a new task has been assigned to them.

In this tutorial, you’ll learn to create a task assignment app using Next.js, Strapi, and Twilio. You’ll learn to use Next.js for building the frontend UI, Strapi for building the backend, and Twilio for sending WhatsApp notifications.

Read the full blog on [Twilio](https://www.twilio.com/blog/build-task-assignment-app-twilio-whatsapp-strapi-next-js).

%[https://www.twilio.com/blog/build-task-assignment-app-twilio-whatsapp-strapi-next-js]

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come across while surfing the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [GitHub](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>Web Development</category>
            <category>twilio</category>
            <category>Next.js</category>
            <category>Strapi</category>
            <category>Tutorial</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1669178508994/a7eQuWR2t.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[How to Setup and Customize Tailwind in Nuxt.js]]></title>
            <link>https://www.ravgeet.in/blog/how-to-setup-and-customize-tailwind-in-nuxtjs</link>
            <guid>https://www.ravgeet.in/blog/how-to-setup-and-customize-tailwind-in-nuxtjs</guid>
            <pubDate>Tue, 15 Nov 2022 04:50:09 GMT</pubDate>
            <description><![CDATA[CSS frameworks like Bootstrap, Bulma, and Materialize are hugely popular among front-end developers. They are a great way to quickly style an application on the set of standard guidelines. However, they are a little difficult to customize and bloated...]]></description>
            <content:encoded><![CDATA[CSS frameworks like Bootstrap, Bulma, and Materialize are hugely popular among front-end developers. They are a great way to quickly style an application on the set of standard guidelines. However, they are a little difficult to customize and bloated for small applications.

Tailwind CSS is a utility-first CSS framework. This means that instead of providing you with ready-made components, Tailwind provides utility-based CSS classes that you can use to style your components. This gives you more flexibility over your design and you can build your own UI framework on top of Tailwind CSS by extending the Tailwind classes.

In this tutorial, you’ll build a portfolio landing page that will have the author’s information and a form to subscribe to a newsletter. You’ll build the front end with Nuxt.js and style it using Tailwind CSS.

Read the full blog on [Mattermost](https://mattermost.com/blog/how-to-set-up-and-customize-tailwind-in-nuxt-js/).

%[https://mattermost.com/blog/how-to-set-up-and-customize-tailwind-in-nuxt-js/]

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come across while surfing the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [GitHub](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>Frontend Development</category>
            <category>Nuxt</category>
            <category>Tailwind CSS</category>
            <category>Web Development</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1669178218615/BkySHylo1.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[REST vs SOAP: why we recommend REST APIs for A2P messaging]]></title>
            <link>https://www.ravgeet.in/blog/rest-vs-soap-why-we-recommend-rest-apis-for-a2p-messaging</link>
            <guid>https://www.ravgeet.in/blog/rest-vs-soap-why-we-recommend-rest-apis-for-a2p-messaging</guid>
            <pubDate>Sun, 06 Nov 2022 12:40:47 GMT</pubDate>
            <description><![CDATA[Many businesses use messaging APIs to simplify communications with stakeholders and customers. Business short message service (SMS) tools enable organizations to implement one-time password (OTP) verification, alerts, reminders, and other types of cu...]]></description>
            <content:encoded><![CDATA[Many businesses use messaging APIs to simplify communications with stakeholders and customers. Business short message service (SMS) tools enable organizations to implement one-time password (OTP) verification, alerts, reminders, and other types of customer support as well as marketing campaigns. They also enable organizations to centrally manage invoicing, human resources, and other internal activities.

Though many applications continue to rely on the SOAP API standard for SMS functionality, the newer REST API standard is actually the better choice. REST offers greater flexibility and speed, while SOAP is more rigid and can be more challenging to learn. This article will explain why we recommend you should choose the REST API standard for your business SMS.

Read the full blog on [Clicksend](https://blog.clicksend.com/soap-vs-rest-api/).

%[https://blog.clicksend.com/soap-vs-rest-api/]

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come across while surfing the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [GitHub](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>APIs</category>
            <category>backend</category>
            <category>REST API</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1667738294135/LrSFOGFN_.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[Authoring NPM Packages with Monorepos]]></title>
            <link>https://www.ravgeet.in/blog/authoring-npm-packages-with-monorepos</link>
            <guid>https://www.ravgeet.in/blog/authoring-npm-packages-with-monorepos</guid>
            <pubDate>Wed, 12 Oct 2022 05:42:16 GMT</pubDate>
            <description><![CDATA[Suppose that you run a software development agency and you want to enforce a common linting rule set and formatting guidelines for all of your JavaScript projects. You could install ESLint and Prettier in each of your projects. However, your company ...]]></description>
            <content:encoded><![CDATA[Suppose that you run a software development agency and you want to enforce a common linting rule set and formatting guidelines for all of your JavaScript projects. You could install ESLint and Prettier in each of your projects. However, your company manages more than a hundred different projects, with custom rules for both ESLint and Prettier. So, if you decide to add or deprecate some rules, you'll have to update the rule sets in all of those projects.

A monorepo can help solve this issue. You can put all of your configuration code in a master repository, publish it as an npm package, and then import the npm package into your projects. Next time you want to change the rules, you only need to alter the monorepo project and the change will be reflected in all of your projects, as they're dependent on the monorepo project.

If you follow the steps in this tutorial, you’ll see how you can publish npm packages using Lerna and keep your packages’ code in a monorepo.

Read the full blog on [Fusebit](https://fusebit.io/blog/npm-packages-with-monorepos/).

%[https://fusebit.io/blog/npm-packages-with-monorepos/]

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come across while surfing the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [GitHub](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>General Programming</category>
            <category>JavaScript</category>
            <category>npm</category>
            <category>development</category>
            <category>project management</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1665551517554/iKzMbsBHz.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[Booking Appointments with Twilio, Notion, and FastAPI]]></title>
            <link>https://www.ravgeet.in/blog/booking-appointments-with-twilio-notion-and-fastapi</link>
            <guid>https://www.ravgeet.in/blog/booking-appointments-with-twilio-notion-and-fastapi</guid>
            <pubDate>Sat, 20 Aug 2022 05:37:16 GMT</pubDate>
            <description><![CDATA[Most businesses run around the concept of appointments. Appointments allow you to schedule different events for different individuals. For example, before seeing a doctor, it might be necessary to book an appointment. Businesses can leverage the powe...]]></description>
            <content:encoded><![CDATA[Most businesses run around the concept of appointments. Appointments allow you to schedule different events for different individuals. For example, before seeing a doctor, it might be necessary to book an appointment. Businesses can leverage the power of WhatsApp to allow their customers to book appointments easily just by sending messages. They can also get updates or check the status of their appointments through WhatsApp messages.

In this tutorial, you’ll learn to use Twilio’s WhatsApp API with the Notion API and FastAPI to create appointments and get their statuses as well. You will use Notion for storing data, Twilio for sending WhatsApp messages, and FastAPI for API and business logic.

Read the full blog on [Twilio](https://www.twilio.com/blog/booking-appointments-twilio-notion-fastapi).

%[https://www.twilio.com/blog/booking-appointments-twilio-notion-fastapi]

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come across while surfing the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [GitHub](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>General Programming</category>
            <category>notion</category>
            <category>twilio</category>
            <category>Python</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1660973475780/g34NX9cus.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Using Python Scripts to Take Screenshots]]></title>
            <link>https://www.ravgeet.in/blog/using-python-scripts-to-take-screenshots</link>
            <guid>https://www.ravgeet.in/blog/using-python-scripts-to-take-screenshots</guid>
            <pubDate>Mon, 15 Aug 2022 08:19:00 GMT</pubDate>
            <description><![CDATA[There are many reasons why developers might want to capture screenshots of web pages. You might want to capture an image generated from dynamic code that you've written, collect screenshots of web pages mentioned in a dataset that you're working with...]]></description>
            <content:encoded><![CDATA[There are many reasons why developers might want to capture screenshots of web pages. You might want to capture an image generated from dynamic code that you've written, collect screenshots of web pages mentioned in a dataset that you're working with, or keep software documentation up to date by automating screenshots using a CI/CD tool.

It can be surprisingly tricky to take screenshots using Python, especially when JavaScript is involved. In this tutorial, you’ll learn to take screenshots of web pages using different approaches and packages in Python. You'll also see how a tailor-made solution like Urlbox can help you easily capture screenshots of websites.

Read the full blog on [Urlbox](https://www.urlbox.io/website-screenshots-python).

%[https://www.urlbox.io/website-screenshots-python]

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come across while surfing the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [Github](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>General Programming</category>
            <category>Python 3</category>
            <category>image processing</category>
            <category>automation</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1659946071193/E0V89rm6k.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Logging in Python]]></title>
            <link>https://www.ravgeet.in/blog/logging-in-python</link>
            <guid>https://www.ravgeet.in/blog/logging-in-python</guid>
            <pubDate>Wed, 10 Aug 2022 07:21:01 GMT</pubDate>
            <description><![CDATA[When an application runs, it performs a tremendous number of tasks. A simple to-do app can have tons of tasks like - user logins, creating to-dos, updating to-dos, deleting to-dos, and duplicating to-dos. These tasks can result in success or may end ...]]></description>
            <content:encoded><![CDATA[When an application runs, it performs a tremendous number of tasks. A simple to-do app can have tons of tasks like - user logins, creating to-dos, updating to-dos, deleting to-dos, and duplicating to-dos. These tasks can result in success or may end up with some errors. Hence, there is a need to monitor events happening and analyze them to identify bottlenecks in the performance of the application. This is where logging is useful.

In this article, you'll learn how to create logs in a Python application using the Python logging module. Logging can help Python developers of all experience levels develop and analyze an application's performance more quickly.

Read the full blog on [Honeybadger](https://www.honeybadger.io/blog/python-logging/).

%[https://www.honeybadger.io/blog/python-logging/]

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come across while surfing the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [Github](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>General Programming</category>
            <category>Python</category>
            <category>logging</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1659769720829/mJn9VISdT.jpeg" length="0" type="image/jpeg"/>
        </item>
        <item>
            <title><![CDATA[Build Client Payment Reminders using Twilio, Notion, and Python]]></title>
            <link>https://www.ravgeet.in/blog/build-client-payment-reminders-using-twilio-notion-and-python</link>
            <guid>https://www.ravgeet.in/blog/build-client-payment-reminders-using-twilio-notion-and-python</guid>
            <pubDate>Thu, 04 Aug 2022 12:45:57 GMT</pubDate>
            <description><![CDATA[Running a business requires payment handling. It doesn't matter whether you are a freelancer or a big corporation, sometimes clients forget to pay their pending dues. If you have a huge list of clients, it makes for a tedious experience to go through...]]></description>
            <content:encoded><![CDATA[Running a business requires payment handling. It doesn't matter whether you are a freelancer or a big corporation, sometimes clients forget to pay their pending dues. If you have a huge list of clients, it makes for a tedious experience to go through the records daily and send them reminders. To solve this issue, you can automate the entire reminder workflow.

In this tutorial, you’ll learn to use Twilio’s WhatsApp API with the Notion API and Python to send payment reminders to your clients at regular intervals. You will use Notion for storing data, Twilio for sending WhatsApp messages, and Python to implement the business logic. We will create three reminders that will be sent 7 days before, 3 days before, 1 day before, and each day after the payment is due.

Read the full blog on [Twilio](https://www.twilio.com/blog/payment-reminders-twilio-notion-python).

%[https://www.twilio.com/blog/payment-reminders-twilio-notion-python]

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come across while surfing the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [Github](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>General Programming</category>
            <category>automation</category>
            <category>Python</category>
            <category>twilio</category>
            <category>notion</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1659616843498/Hmy2eM7vp.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Build and Deploy a Nuxt3 app to Netlify]]></title>
            <link>https://www.ravgeet.in/blog/build-and-deploy-a-nuxt3-app-to-netlify</link>
            <guid>https://www.ravgeet.in/blog/build-and-deploy-a-nuxt3-app-to-netlify</guid>
            <pubDate>Thu, 07 Jul 2022 06:30:00 GMT</pubDate>
            <description><![CDATA[Imagine you want to build and deploy a Nuxt3 app on Netlify. Because custom scripts are not allowed on Netlify, you will not be able to perform custom tasks like automated testing before deploying the website to your Jamstack hosting platform.
That i...]]></description>
            <content:encoded><![CDATA[Imagine you want to build and deploy a Nuxt3 app on Netlify. Because custom scripts are not allowed on Netlify, you will not be able to perform custom tasks like automated testing before deploying the website to your Jamstack hosting platform.

That is where continuous integration/continuous deployment comes in. With a CI/CD system, you can run the kind of automated tests that create successful deployments. In this tutorial, I will lead you through building a Nuxt3 app, writing automated tests for it, and deploying it on Netlify.

Read the full blog on [CircleCI](https://circleci.com/blog/deploy-nuxt3-app-to-netlify/).

%[https://circleci.com/blog/deploy-nuxt3-app-to-netlify/]

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come across while surfing the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [Github](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>Nuxt</category>
            <category>JavaScript</category>
            <category>Netlify</category>
            <category>automation</category>
            <category>ci-cd</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1656743404450/EDqPJbZ_O.avif" length="0" type="image/avif"/>
        </item>
        <item>
            <title><![CDATA[What is End-to-End Testing?]]></title>
            <link>https://www.ravgeet.in/blog/what-is-end-to-end-testing</link>
            <guid>https://www.ravgeet.in/blog/what-is-end-to-end-testing</guid>
            <pubDate>Sat, 02 Jul 2022 06:27:46 GMT</pubDate>
            <description><![CDATA[End-to-end testing, also known as E2E testing, is a methodology used for ensuring that applications behave as expected and that the flow of data is maintained for all kinds of user tasks and processes. This type of testing approach starts from the en...]]></description>
            <content:encoded><![CDATA[End-to-end testing, also known as E2E testing, is a methodology used for ensuring that applications behave as expected and that the flow of data is maintained for all kinds of user tasks and processes. This type of testing approach starts from the end user’s perspective and simulates a real-world scenario. For example, on a sign-up form, you can expect a user to perform one or more of these actions:

- Enter a blank email and password
- Enter a valid email and password
- Enter an invalid email and password
- Click a sign-up button

You can use end-to-end testing to verify that all these actions work as a user might expect.

End-to-end testing may sound comprehensive, but there are many other testing methods that you should use with it to create a robust continuous integration practice.

Read the full blog on [CircleCI](https://circleci.com/blog/what-is-end-to-end-testing/).

%[https://circleci.com/blog/what-is-end-to-end-testing/]

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come across while surfing the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [Github](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>Testing</category>
            <category>ci-cd</category>
            <category>automation</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1656742896526/dQFF8q5P1.avif" length="0" type="image/avif"/>
        </item>
        <item>
            <title><![CDATA[Handling Undo/Redo Functions in Rich Text Editors]]></title>
            <link>https://www.ravgeet.in/blog/handling-undoredo-functions-in-rich-text-editors</link>
            <guid>https://www.ravgeet.in/blog/handling-undoredo-functions-in-rich-text-editors</guid>
            <pubDate>Mon, 06 Jun 2022 06:30:00 GMT</pubDate>
            <description><![CDATA[If you’ve ever written a blog or worked with a Content Management System (CMS), there’s a good chance you’ve heard about rich text editors – popularly known as WYSIWYG (What You See Is What You Get) editors. 
A rich text editor allows users to enter ...]]></description>
            <content:encoded><![CDATA[If you’ve ever written a blog or worked with a Content Management System (CMS), there’s a good chance you’ve heard about rich text editors – popularly known as WYSIWYG (What You See Is What You Get) editors. 

A rich text editor allows users to enter text and formatting via a GUI. It converts input into HTML behind the scenes. Why is this important? It enables non-technical users to create web-ready code. Having been on the market since the late 1990s, rich text editors have progressively evolved to support complex features like undo/redo.

Undo and redo operations are a must-have feature in any rich text editor – they’re a user's safety net. For a great user experience (UX), users need to solve their editing problems in a rich text editor.

In this article, you'll find out about the complexity of creating and maintaining the undo/redo functionality, and see how the TinyMCE rich text editor makes it easy.

Read the full blog on [Tiny.Cloud](https://www.tiny.cloud/blog/undo-function-handling/).

%[https://www.tiny.cloud/blog/undo-function-handling/]

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come across while surfing on the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [Github](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>Text Editors</category>
            <category>JavaScript</category>
            <category>CSS</category>
            <category>HTML5</category>
            <category>General Programming</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1653992846783/2Zyrj2Zz9.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[The Complete List of OAuth 2 Grants]]></title>
            <link>https://www.ravgeet.in/blog/the-complete-list-of-oauth-2-grants</link>
            <guid>https://www.ravgeet.in/blog/the-complete-list-of-oauth-2-grants</guid>
            <pubDate>Tue, 31 May 2022 10:25:07 GMT</pubDate>
            <description><![CDATA[Authorization is necessary to protect resources from malicious use. When the Internet Engineering Task Force (IETF) drafted internet protocols and rules, it also planned out different methods to protect and access resources on a server. These efforts...]]></description>
            <content:encoded><![CDATA[Authorization is necessary to protect resources from malicious use. When the Internet Engineering Task Force (IETF) drafted internet protocols and rules, it also planned out different methods to protect and access resources on a server. These efforts led to OAuth 1.0 and later OAuth 2.0.

The OAuth 2.0 specification is an authorization framework containing a number of methods, or grants, by which a client application can get an access token. The access token can be presented to an API endpoint, which can then examine it to determine validity and permissions levels. Each grant type is designed for a particular use case.

OAuth 2.0 focuses on the authorization. There are other protocols like OpenID Connect (OIDC) that focus on authentication. OIDC allows the software to access login and profile information about the logged-in user.

This article will go through all the different OAuth 2 grant types and explain the flow for each so that you can determine which is the best fit and safely use it in your applications.

Read the full blog on [FusionAuth](https://fusionauth.io/learn/expert-advice/oauth/complete-list-oauth-grants).

%[https://fusionauth.io/learn/expert-advice/oauth/complete-list-oauth-grants]

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come across while surfing on the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [Github](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>APIs</category>
            <category>authentication</category>
            <category>Auth0</category>
            <category>authorization</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1653992562934/1sAPvwjo1.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Creating Golang CRON Jobs]]></title>
            <link>https://www.ravgeet.in/blog/creating-golang-cron-jobs</link>
            <guid>https://www.ravgeet.in/blog/creating-golang-cron-jobs</guid>
            <pubDate>Mon, 23 May 2022 09:07:17 GMT</pubDate>
            <description><![CDATA[Scheduled tasks allow you to run specific code at a specified interval of time and are primarily used within a CI/CD system to perform a variety of operations like nightly builds, GitHub repository cleanup, newsletters, and service monitoring, among ...]]></description>
            <content:encoded><![CDATA[Scheduled tasks allow you to run specific code at a specified interval of time and are primarily used within a CI/CD system to perform a variety of operations like nightly builds, GitHub repository cleanup, newsletters, and service monitoring, among others. You can use scheduled jobs to send notifications when a process succeeds or fails and to perform batch tasks without any human involvement.

In this article, you'll learn to create CRON jobs in Golang, a statically typed, compiled programming language designed by engineers at Google. Golang contains the best features from C and Python, like memory safety, automatic garbage collection, structural typing, and concurrency, to name a few. Specifically, you'll learn to use **gocron** and **cron.v2** to schedule tasks, explore the limitations of using these packages, and then consider a serverless and easy-to-use approach to task scheduling using Airplane.

Read the full blog on [Airplane](https://www.airplane.dev/blog/creating-golang-cron-jobs).

%[https://www.airplane.dev/blog/creating-golang-cron-jobs]

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come across while surfing on the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [Github](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>Go Language</category>
            <category>automation</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1653897302940/X_uA5p0pb.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[Handling Pagination in Strapi v4 with SvelteKit]]></title>
            <link>https://www.ravgeet.in/blog/handling-pagination-in-strapi-v4-with-sveltekit</link>
            <guid>https://www.ravgeet.in/blog/handling-pagination-in-strapi-v4-with-sveltekit</guid>
            <pubDate>Thu, 19 May 2022 06:58:54 GMT</pubDate>
            <description><![CDATA[If you use any kind of web or mobile application, you may have come across a data table that lets you view data by breaking it up into multiple pages. In the world of software development, this is known as pagination.
Pagination is an optimization te...]]></description>
            <content:encoded><![CDATA[If you use any kind of web or mobile application, you may have come across a data table that lets you view data by breaking it up into multiple pages. In the world of software development, this is known as pagination.

Pagination is an optimization technique that is used both on the frontend and backend to enhance the performance of your applications. With pagination, you can skip to the desired page and view the results for that particular page without loading any additional data. In this tutorial, you’ll learn how to work with Strapi for the backend and implement the pagination controls UI by building the frontend in Svelte.

Read the full blog on [Strapi](https://strapi.io/blog/handling-pagination-in-strapi-v4-with-svelte-kit).

%[https://strapi.io/blog/handling-pagination-in-strapi-v4-with-svelte-kit]

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come across while surfing on the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [Github](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>Svelte</category>
            <category>Strapi</category>
            <category>UI</category>
            <category>Frontend Development</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1652943064782/rDpOo5xfa.jpeg" length="0" type="image/jpeg"/>
        </item>
        <item>
            <title><![CDATA[Using Custom Controllers in Strapi to Power a Next.js App]]></title>
            <link>https://www.ravgeet.in/blog/using-custom-controllers-in-strapi-to-power-a-nextjs-app</link>
            <guid>https://www.ravgeet.in/blog/using-custom-controllers-in-strapi-to-power-a-nextjs-app</guid>
            <pubDate>Fri, 13 May 2022 09:53:59 GMT</pubDate>
            <description><![CDATA[Strapi continues to be the most popular free, open-source, headless CMS, and, recently, it released v4. Built using Node.js with support for TypeScript, Strapi allows developers to perform CRUD operations using either REST or GraphQL APIs.
The best p...]]></description>
            <content:encoded><![CDATA[Strapi continues to be the most popular free, open-source, headless CMS, and, recently, it released v4. Built using Node.js with support for TypeScript, Strapi allows developers to perform CRUD operations using either REST or GraphQL APIs.

The best part of Strapi is that it allows users to customize its behavior, whether for the admin panel or the core business logic of your backend. You can modify its default controllers to include your own logic. For example, you might want to send an email when a new order is created.

In this tutorial, you’ll learn how to build a messaging app with Strapi on the backend and Next.js on the frontend. For this app, you’ll customize the default controllers to set up your own business logic.

Read the full blog on [Strapi](https://strapi.io/blog/using-custom-controllers-to-power-a-next-js-app).

%[https://strapi.io/blog/using-custom-controllers-to-power-a-next-js-app]

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come across while surfing on the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [Github](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>JavaScript</category>
            <category>React</category>
            <category>Next.js</category>
            <category>Strapi</category>
            <category>General Programming</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1652435500816/FNjtamc9K.jpeg" length="0" type="image/jpeg"/>
        </item>
        <item>
            <title><![CDATA[Build a Chrome Extension in Next.js and Notion API]]></title>
            <link>https://www.ravgeet.in/blog/build-a-chrome-extension-in-nextjs-and-notion-api</link>
            <guid>https://www.ravgeet.in/blog/build-a-chrome-extension-in-nextjs-and-notion-api</guid>
            <pubDate>Fri, 15 Apr 2022 04:12:16 GMT</pubDate>
            <description><![CDATA[Chrome extensions are a great way to customize your browsing experience. Most of the time, Chrome extensions need to be reactive and this is where building the extension in vanilla JavaScript can be a painful experience. To overcome this shortcoming,...]]></description>
            <content:encoded><![CDATA[Chrome extensions are a great way to customize your browsing experience. Most of the time, Chrome extensions need to be reactive and this is where building the extension in vanilla JavaScript can be a painful experience. To overcome this shortcoming, you can use a JavaScript-based front-end framework like Next.js or Nuxt.js to build your Chrome extensions.

In this tutorial, you’ll learn to build a Chrome extension using Next.js. For this tutorial, you’ll build a Chrome extension that allows you to tag and save web links to a Notion database.

Read the full blog on [Bird Eats Bug](https://birdeatsbug.com/blog/build-a-chrome-extension-in-next-js-and-notion-api).

%[https://birdeatsbug.com/blog/build-a-chrome-extension-in-next-js-and-notion-api]

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come across while surfing on the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [Github](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>General Programming</category>
            <category>chrome extension</category>
            <category>Next.js</category>
            <category>APIs</category>
            <category>React</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1649995838315/PQQNm3hTr.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Building a Realtime Chat App with React, Laravel, and WebSockets]]></title>
            <link>https://www.ravgeet.in/blog/building-a-realtime-chat-app-with-react-laravel-and-websockets</link>
            <guid>https://www.ravgeet.in/blog/building-a-realtime-chat-app-with-react-laravel-and-websockets</guid>
            <pubDate>Fri, 08 Apr 2022 08:34:15 GMT</pubDate>
            <description><![CDATA[You use real-time communication every day. It is the simultaneous exchange of information between a sender and a receiver with almost zero latency. Internet, landlines, mobile/cell phones, instant messaging (IM), internet relay chat, videoconferencin...]]></description>
            <content:encoded><![CDATA[You use real-time communication every day. It is the simultaneous exchange of information between a sender and a receiver with almost zero latency. Internet, landlines, mobile/cell phones, instant messaging (IM), internet relay chat, videoconferencing, teleconferencing, and robotic telepresence are all examples of real-time communication systems.

In this tutorial, you’ll learn how to build a real-time public chat app using React.js, Laravel, and Ably. You’ll use React.js to build the frontend/UI and Laravel to interact with Ably Realtime APIs to facilitate real-time communication. Anyone on the internet would be able to use this app to post messages to a public chat room and talk anonymously with other connected users. By building this kind of application, you’ll learn about the relevant concepts for building applications that need real-time data transfer.

Read the full blog on [Ably](https://ably.com/blog/building-a-realtime-chat-app-with-react-laravel-and-websockets).

%[https://ably.com/blog/building-a-realtime-chat-app-with-react-laravel-and-websockets]

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come across while surfing on the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [Github](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>General Programming</category>
            <category>React</category>
            <category>Laravel</category>
            <category>messaging</category>
            <category>Tutorial</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1649406701576/1toJt0kA1.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Starting my First Full-time role as a Software Engineer]]></title>
            <link>https://www.ravgeet.in/blog/starting-my-first-full-time-role-as-a-software-engineer</link>
            <guid>https://www.ravgeet.in/blog/starting-my-first-full-time-role-as-a-software-engineer</guid>
            <pubDate>Mon, 07 Feb 2022 12:01:47 GMT</pubDate>
            <description><![CDATA[After applying to over 50+ remote jobs and getting rejected in 3 of them, I've finally got my first full-time role as a Software Engineer. 🎉🎉
The funny thing is I was offered a role in a company that I didn't even apply to. I want to share my exper...]]></description>
            <content:encoded><![CDATA[After applying to over 50+ remote jobs and getting rejected in 3 of them, I've finally got my first full-time role as a **Software Engineer**. 🎉🎉

The funny thing is I was offered a role in a company that I didn't even apply to. I want to share my experience with anyone that is looking for a job.

Here's the timeline of how it happened:

- **May 2020**: Began my freelancing career along with my Master's in Computer Science and Engineering.

- **June 2021**: I got an email from a US-based company for a Frontend Engineer freelance contract. I accepted the offer.

- **November 2021**: Since I was going to postgraduate in 6 months, I started applying for a remote full-time role.

- **November 2021 - February 2022**: Most of the time, I didn't even get a first-round interview, and in three I got rejected because of my experience even though I've worked in Open Source and made large ECommerce websites.

- **1 February 2022**: I got a call from the US-based company I was working for as a freelancer, and they asked if I wanted to continue with them as a full-time employee?

I couldn't believe it 😲. I didn't even ask them. No interviews, no technical tests, no meetings. They handed me a generous offer and I signed the legal papers on the same day.

I'm really happy how things have panned out. I'm really excited to start my career as a full-time Software Engineer at [CloudAnswers](https://cloudanswers.com/).

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come across while surfing on the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [Github](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*
]]></content:encoded>
            <category>Learning Journey</category>
            <category>jobs</category>
            <category>Career</category>
            <category>Software Engineering</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1644234973701/BBSL1KB6o.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Implementing Audio in a Podcast App with Strapi]]></title>
            <link>https://www.ravgeet.in/blog/implementing-audio-in-a-podcast-app-with-strapi</link>
            <guid>https://www.ravgeet.in/blog/implementing-audio-in-a-podcast-app-with-strapi</guid>
            <pubDate>Tue, 25 Jan 2022 10:00:00 GMT</pubDate>
            <description><![CDATA[Podcasts have exploded in popularity, and platforms including Google Podcasts and Spotify offer content creators a way to communicate their thoughts with listeners around the world. If you’d like to join them, you can create your own podcast app usin...]]></description>
            <content:encoded><![CDATA[Podcasts have exploded in popularity, and platforms including Google Podcasts and Spotify offer content creators a way to communicate their thoughts with listeners around the world. If you’d like to join them, you can create your own podcast app using Strapi and a frontend of your choice.

In this tutorial, you’ll learn to implement audio in a podcast app. You’ll build your app in Nuxt.js and manage your podcast content in the Strapi CMS.

Read the full blog on [Strapi](https://strapi.io/blog/implementing-audio-in-a-podcast-app-with-strapi).

%[https://strapi.io/blog/implementing-audio-in-a-podcast-app-with-strapi]

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come across while surfing on the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [Github](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>headless cms</category>
            <category>Nuxt</category>
            <category>Vue.js</category>
            <category>JavaScript</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1643104712881/WTLli67Cm.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Handling Previews in a Headless Architecture - Strapi and Next.js]]></title>
            <link>https://www.ravgeet.in/blog/handling-previews-in-a-headless-architecture-strapi-and-nextjs</link>
            <guid>https://www.ravgeet.in/blog/handling-previews-in-a-headless-architecture-strapi-and-nextjs</guid>
            <pubDate>Thu, 06 Jan 2022 09:57:26 GMT</pubDate>
            <description><![CDATA[There is an ongoing shift in content management from traditional CMS to headless CMS. A headless CMS allows you to completely separate your content management system from the presentation layer. The content is made available via API and can be consum...]]></description>
            <content:encoded><![CDATA[There is an ongoing shift in content management from traditional CMS to headless CMS. A headless CMS allows you to completely separate your content management system from the presentation layer. The content is made available via API and can be consumed in any kind of frontend, from websites to mobile apps.

Using headless CMSs has opened up a new way of building websites, known as pre-rendering. It is one of the best-known techniques in Jamstack, in which the website is compiled into a set of static assets like prebuilt HTML, CSS, and JavaScript files with the help of a static site generator (SSG). During the build time, the files are created by collecting the data from a headless CMS. These files are cached to a content delivery network (CDN) and served to a user on each request from the nearest CDN node. This improves speed and response times and reduces hosting costs.

However, content creators need to preview their content before publishing it to production, meaning they need to wait for an entire build to complete before they can view their content. To solve this problem, a preview mode allows editors to view their changes on the fly.

In this tutorial, you’ll learn to implement a preview system when working with a headless CMS like Strapi. You’ll implement the frontend in Next.js for creating content previews.

Read the full blog on [Strapi](https://strapi.io/blog/handling-previews-in-a-headless-architecture).

%[https://strapi.io/blog/handling-previews-in-a-headless-architecture]

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come across while surfing on the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [Github](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>cms</category>
            <category>Next.js</category>
            <category>JavaScript</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1641462626636/Idg9ulI4l.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Creating an App Information Component in Nuxt]]></title>
            <link>https://www.ravgeet.in/blog/creating-an-app-information-component-in-nuxt</link>
            <guid>https://www.ravgeet.in/blog/creating-an-app-information-component-in-nuxt</guid>
            <pubDate>Tue, 21 Dec 2021 06:08:21 GMT</pubDate>
            <description><![CDATA[You must have seen multiple apps which show the app’s information like app version and last updated at time in their footers or via a floating action button. In this tutorial, you’ll learn to create a component to show such kind of information in a N...]]></description>
            <content:encoded><![CDATA[You must have seen multiple apps which show the app’s information like **app version** and **last updated at time** in their footers or via a floating action button. In this tutorial, you’ll learn to create a component to show such kind of information in a Nuxt app.

Read the full blog on [RavSam](https://www.ravsam.in/blog/create-an-app-information-component-in-nuxt/).

%[https://www.ravsam.in/blog/create-an-app-information-component-in-nuxt/]

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come across while surfing on the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [Github](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*
]]></content:encoded>
            <category>JAMstack</category>
            <category>Nuxt</category>
            <category>Vue.js</category>
            <category>JavaScript</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1640066797991/bCSdHG9DA.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[React vs Vue: What is the Best Framework for your Project in 2022?]]></title>
            <link>https://www.ravgeet.in/blog/react-vs-vue-what-is-the-best-framework-for-your-project</link>
            <guid>https://www.ravgeet.in/blog/react-vs-vue-what-is-the-best-framework-for-your-project</guid>
            <pubDate>Thu, 16 Dec 2021 06:32:22 GMT</pubDate>
            <description><![CDATA[React and Vue are rising stars in the JavaScript front-end frameworks ecosystem. React is backed by Facebook, and Vue is completely a community-driven project. The choices that React developers and Vue developers make have consequences as the project...]]></description>
            <content:encoded><![CDATA[React and Vue are rising stars in the JavaScript front-end frameworks ecosystem. React is backed by Facebook, and Vue is completely a community-driven project. The choices that React developers and Vue developers make have consequences as the project size grows, but they may not realize them when they first pick up on over the other.

Both React and Vue can accomplish the same thing. However, they provide different paths to reach the final destination. In this article, you'll look at different comparisons that will help you differentiate between React and Vue and ultimately help you decide which one you should choose in your future projects.

Read the full blog on [Adeva](https://adevait.com/javascript-developers/react-vs-vue).

%[https://adevait.com/javascript-developers/react-vs-vue]

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come across while surfing on the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [Github](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*
]]></content:encoded>
            <category>JavaScript</category>
            <category>React</category>
            <category>Vue.js</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1639636323417/uTBBKDKgRF.jpeg" length="0" type="image/jpeg"/>
        </item>
        <item>
            <title><![CDATA[Creating a Balance Reminder with Vonage Account API and Google Apps]]></title>
            <link>https://www.ravgeet.in/blog/creating-a-balance-reminder-with-vonage-account-api-and-google-apps</link>
            <guid>https://www.ravgeet.in/blog/creating-a-balance-reminder-with-vonage-account-api-and-google-apps</guid>
            <pubDate>Wed, 08 Dec 2021 10:50:02 GMT</pubDate>
            <description><![CDATA[Being a freelancer, I have helped a couple of local businesses in India implement Vonage products. Recently, one of my clients asked if they can get a reminder email when the Vonage balance is below a specified limit as they don't want to hamper thei...]]></description>
            <content:encoded><![CDATA[Being a freelancer, I have helped a couple of local businesses in India implement Vonage products. Recently, one of my clients asked if they can get a reminder email when the Vonage balance is below a specified limit as they don't want to hamper their operations because of insufficient balance. Almost all of my clients use Google Workspace, so I decided to create an integration of Vonage and Google Apps Script to create this workflow.

Google Apps Script allows us to manage all of Google apps using one platform in the cloud. The best part is that the authentication is baked into the platforms and many businesses in the market use Google Workspace – formerly known as G-Suite.

In this blog post, you'll learn how to create custom notifications when your Vonage Account balance is below a specified limit. The post is aimed at those Vonage developers who want to manage their client base effectively by sending them reminders about the Vonage Account balance.

Read the full blog on [Vonage](https://learn.vonage.com/blog/2021/06/08/create-a-balance-reminder-with-vonage-account-api-and-google-apps/).

%[https://learn.vonage.com/blog/2021/06/08/create-a-balance-reminder-with-vonage-account-api-and-google-apps/]

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come across while surfing on the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [Github](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>automation</category>
            <category>JavaScript</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1638960379559/IV33WR0ez.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Building a News Aggregator App using Strapi and Nuxtjs]]></title>
            <link>https://www.ravgeet.in/blog/build-a-news-aggregator-app-using-strapi-and-nuxtjs</link>
            <guid>https://www.ravgeet.in/blog/build-a-news-aggregator-app-using-strapi-and-nuxtjs</guid>
            <pubDate>Sun, 05 Dec 2021 09:13:15 GMT</pubDate>
            <description><![CDATA[If you are an avid reader, you might have a News Aggregator app installed on your device. Wouldn't it be awesome to create your own News Aggregator app that you can control and customize according to your needs?
This tutorial aims to learn about Stra...]]></description>
            <content:encoded><![CDATA[If you are an avid reader, you might have a News Aggregator app installed on your device. Wouldn't it be awesome to create your own News Aggregator app that you can control and customize according to your needs?

This tutorial aims to learn about Strapi and Nuxt.js by building a News Aggregator app with Strapi and Nuxt.js. In this app, you'll:

- Learn to set up Strapi Collection types
- Learn to set up Frontend app using Nuxt.js
- Use CRON jobs to fetch news items automatically
- Add Search capabilities
- Register subscribers

Read the full blog on [Strapi](https://strapi.io/blog/build-a-news-aggregator-app-using-strapi-and-nuxtjs).

%[https://strapi.io/blog/build-a-news-aggregator-app-using-strapi-and-nuxtjs]

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come across while surfing on the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [Github](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>Nuxt</category>
            <category>Vue.js</category>
            <category>JAMstack</category>
            <category>JavaScript</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1638690736178/APE7q0Vtl.jpeg" length="0" type="image/jpeg"/>
        </item>
        <item>
            <title><![CDATA[Converting and Optimizing Images From the Command Line]]></title>
            <link>https://www.ravgeet.in/blog/converting-and-optimizing-images-from-the-command-line</link>
            <guid>https://www.ravgeet.in/blog/converting-and-optimizing-images-from-the-command-line</guid>
            <pubDate>Sat, 27 Nov 2021 05:30:05 GMT</pubDate>
            <description><![CDATA[Images take up to 50% of the total size of an average web page. And if images are not optimized, users end up downloading extra bytes. And if they’re downloading extra bytes, the site not only takes that much more time to load, but users are using mo...]]></description>
            <content:encoded><![CDATA[Images take up to 50% of the total size of an average web page. And if images are not optimized, users end up downloading extra bytes. And if they’re downloading extra bytes, the site not only takes that much more time to load, but users are using more data, both of which can be resolved, at least in part, by optimizing the images before they are downloaded.

In this tutorial, you'll learn to write bash scripts that create and optimize images in different image formats, targeting the most common formats, including JPG, PNG, WebP, and SVG.

Read the full blog on [CSS Tricks](https://css-tricks.com/converting-and-optimizing-images-from-the-command-line/).

%[https://css-tricks.com/converting-and-optimizing-images-from-the-command-line/]

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I across while surfing on the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [Github](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>Bash</category>
            <category>Linux</category>
            <category>images</category>
            <category>Web Perf</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1637990799063/qcrTIwh7_.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Back to Basics: Conditional Logic with Python if else if]]></title>
            <link>https://www.ravgeet.in/blog/back-to-basics-conditional-logic-with-python-if-else-if</link>
            <guid>https://www.ravgeet.in/blog/back-to-basics-conditional-logic-with-python-if-else-if</guid>
            <pubDate>Sat, 20 Nov 2021 06:09:28 GMT</pubDate>
            <description><![CDATA[Whether you are new to Python programming or returning to it after a break, you may need to learn or re-learn about decision-making and branching statements in Python.
In this tutorial, you will learn about different if, else if, else scenarios that ...]]></description>
            <content:encoded><![CDATA[Whether you are new to Python programming or returning to it after a break, you may need to learn or re-learn about decision-making and branching statements in Python.

In this tutorial, you will learn about different if, else if, else scenarios that may arise while writing a Python program.

Read the full blog on [Adam The Automator](https://adamtheautomator.com/python-if-else-if/).

%[https://adamtheautomator.com/python-if-else-if/]

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I across while surfing on the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [Github](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>General Programming</category>
            <category>Python</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1637388436077/vCC3xlzKJ.jpeg" length="0" type="image/jpeg"/>
        </item>
        <item>
            <title><![CDATA[How to Test Your NGINX Configuration Before Screwing it Up]]></title>
            <link>https://www.ravgeet.in/blog/how-to-test-your-nginx-configuration-before-screwing-it-up</link>
            <guid>https://www.ravgeet.in/blog/how-to-test-your-nginx-configuration-before-screwing-it-up</guid>
            <pubDate>Thu, 18 Nov 2021 08:32:55 GMT</pubDate>
            <description><![CDATA[A little invalid change to your Nginx configuration can bring down your entire server. Before performing changes to the Nginx configuration, it is a safe idea to test the changes and then reload the server.
In this tutorial, you'll learn to get start...]]></description>
            <content:encoded><![CDATA[A little invalid change to your Nginx configuration can bring down your entire server. Before performing changes to the Nginx configuration, it is a safe idea to test the changes and then reload the server.

In this tutorial, you'll learn to get started ensuring you never take production down again!

Read the full blog on [Adam The Automator](https://adamtheautomator.com/nginx-test-config/).

%[https://adamtheautomator.com/nginx-test-config/]

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I come across while surfing on the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [Github](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>nginx</category>
            <category>server</category>
            <category>backend</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1637224148722/4jWFRRdha.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[Using Bash Sort to Sort Files Like a Boss]]></title>
            <link>https://www.ravgeet.in/blog/using-bash-sort-to-sort-files-like-a-boss</link>
            <guid>https://www.ravgeet.in/blog/using-bash-sort-to-sort-files-like-a-boss</guid>
            <pubDate>Sun, 14 Nov 2021 08:56:00 GMT</pubDate>
            <description><![CDATA[Are you looking out for a way to organize your files and perform some operations on them? There are many instances in programming where you need to sort some data, such as a list of files. Sorting files with the Bash sort and ls commands will help yo...]]></description>
            <content:encoded><![CDATA[Are you looking out for a way to organize your files and perform some operations on them? There are many instances in programming where you need to sort some data, such as a list of files. Sorting files with the Bash sort and ls commands will help you keep things organized.

In this tutorial, you will learn the fundamentals of sorting files and file contents.

I wrote this post to help you learn the fundamentals of sorting files and file contents using Shell scripting.

Read the full blog on [Adam The Automator](https://adamtheautomator.com/bash-sort/).

%[https://adamtheautomator.com/bash-sort/]

Thanks for reading 💜

---

I publish a [monthly newsletter](https://www.ravsam.in/newsletter/) in which I share personal stories, things that I am working on, what is happening in the world of tech, and some interesting dev-related posts which I across while surfing on the web.

Connect with me through [Twitter](https://twitter.com/ravgeetdhillon) • [LinkedIn](https://linkedin.com/in/ravgeetdhillon) • [Github](https://github.com/ravgeetdhillon) or send me an [Email](mailto:ravgeetdhillon@gmail.com).

— [Ravgeet](https://www.ravgeet.in/), *Full Stack Developer and Technical Content Writer*]]></content:encoded>
            <category>Bash</category>
            <category>automation</category>
            <category>Linux</category>
            <enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1636879979008/0sfUJ2ub-n.jpeg" length="0" type="image/jpeg"/>
        </item>
    </channel>
</rss>