The Weird Claude Code Error That Ate My Afternoon (and How I Fixed It)

Today, while I was deep in the middle of handing off some work between Claude Code sessions, I got this weird error out of nowhere:

API Error: 400 messages.7.content.3: `thinking` or `redacted_thinking`
blocks in the latest assistant message cannot be modified. These blocks
must remain as they were in the original response.

I’d never seen it before, and at first nothing I did made it go away — every time I thought I’d fixed it, it came right back, sometimes pointing at a different spot in the conversation. So I went down the rabbit hole, figured out what was actually happening, and I’m writing it up here in case you hit the same wall. The good news: it’s almost always fixable, and you usually don’t lose your work.

What the error means

When thinking is enabled, Claude’s responses include thinking (or redacted_thinking) blocks. These carry a cryptographic signature, and the API requires them to be passed back byte-for-byte unchanged — same text, same signature, same position in the message. If a thinking block gets altered, reordered, truncated, or loses its signature, the next API call is rejected with a 400 pointing at the offending message and content index (e.g. messages.7.content.3).

In Claude Code, this almost never comes from anything you typed. It comes from the session history file the CLI replays on each turn.

Two flavours of this bug (figuring out which you have)

This was the part that tripped me up, so it’s worth slowing down on. There are two versions, and they need different fixes.

1. A fixed error index that never moves. The same messages.N.content.M every time. This is classic history corruption — one bad block sitting in your session file, often from an interrupted turn, a crash, or switching models/thinking settings mid-conversation. Cleaning the file fixes it.

2. An error index that keeps moving (7.content.3, then 1.content.18, then 3.content.41…). This means the bad block is being regenerated on every turn, not sitting statically in old history. The usual cause is a parallel-tool-call cascade: Claude fires several tool calls at once, one gets cancelled, and the turn ends up with thinking blocks tangled up with orphaned/cancelled tool calls. Cleaning the file only unblocks you until the session re-enters the same spiral. The durable fix is to not resume that session.

The fixes, in order
Step 0 — Update Claude Code first

Several fixes have shipped for exactly this class of bug (thinking blocks orphaned by interrupted or parallel tool calls on replay).

claude update
# or
npm install -g @anthropic-ai/claude-code
Step 1 — The easy escape hatch: clear or compact

If you don’t need to preserve the conversation, /clear removes the bad block entirely by starting fresh. If you want to keep your thread of work, /compact summarises the history into a clean context and drops the corrupt block. Both are one command and solve the majority of cases.

Step 2 — Turn thinking off (fastest unblock)

If the API isn’t enforcing signature integrity, the malformed block stops being rejected. Toggle thinking off (via /configor your version’s thinking toggle) to get moving immediately. You can turn it back on later.

Step 3 — Repair the session file in place

If you’re mid-handoff and can’t clear, you can surgically remove thinking blocks from the session file. The single most important rule: fully quit Claude Code before editing. While the process is alive, it owns the file and will overwrite your edits from memory on the next turn — re-introducing the exact block you just removed. This is the number-one reason people think the fix “didn’t work.”

# 1. Make sure nothing is running
pkill -f claude; sleep 1; pgrep -fl claude   # should print nothing

# 2. Find your project's session directory
ls -lt ~/.claude/projects/<your-project-hash>/

# 3. See which session files still contain thinking blocks
for F in ~/.claude/projects/<your-project-hash>/*.jsonl; do
  echo "$(grep -c '"thinking"' "$F")  $F"
done

Then strip thinking blocks from the affected file(s). This Python snippet removes them and drops any assistant turn left empty afterward (the API rejects empty assistant messages too):

F="path/to/session.jsonl"
cp "$F" "$F.bak"   # always back up

python3 - "$F" <<'EOF'
import json, sys
path = sys.argv[1]
out, removed, dropped = [], 0, 0
for ln in open(path).read().splitlines():
    if not ln.strip(): continue
    try: obj = json.loads(ln)
    except json.JSONDecodeError:
        out.append(ln); continue
    if obj.get("type") == "assistant":
        c = obj.get("message", {}).get("content")
        if isinstance(c, list):
            new = [b for b in c
                   if not (isinstance(b, dict)
                           and b.get("type") in ("thinking", "redacted_thinking"))]
            removed += len(c) - len(new)
            if len(new) == 0:
                dropped += 1
                continue
            obj["message"]["content"] = new
    out.append(json.dumps(obj, ensure_ascii=False))
open(path, "w").write("\n".join(out) + "\n")
print(f"removed {removed} thinking block(s), dropped {dropped} empty turn(s)")
EOF

Removing a whole thinking block is allowed — the API only objects to modified ones. After editing, verify it’s clean and stays clean, then relaunch:

grep -c '"thinking"' "$F"   # expect 0
A few gotchas worth knowing
  • Your projects can have many session files. This one got me. Claude Code resumes the most-recently-modified one, and --continue auto-picks by timestamp. I kept cleaning a file and the error kept coming — turns out I was editing the wrong session. Check every .jsonl in the project directory, not just the obvious one.
  • A moving error index = a recurring trigger. Don’t just clean the file; avoid resuming the session that keeps producing the bad turn. Start fresh from a handoff doc or /compact instead.
  • The error path is an API-message index, not a JSONL line number. The history file includes user turns, tool results and summaries that get filtered/renumbered before the request is built, so messages.7 is rarely line 7 of the file. This is why blanket-cleaning all assistant turns is more reliable than hand-editing one line.
TL;DR (what I wish I’d known at the start)

The thinking-block 400 is a session-state problem in Claude Code, not your data. Update the CLI, then /clear or /compact if you can. If you have to preserve the session, quit Claude Code completely before touching anything, strip the thinking blocks from the session JSONL, verify it’s clean, and relaunch. And if the error index keeps moving between attempts, stop trying to resume that session — the bad turn is being regenerated each time, so just start fresh from your handoff notes.

That last point is what finally got me unstuck. Hope it saves you the afternoon it cost me.

The Fig Tree and the Temple: Leaves Without Fruit

One of the passages that puzzled me for a long time was the account in the Gospels of Gospel of Mark and Gospel of Matthew where Jesus curses a fig tree, and the tree withers away from its roots.

At first, this was very confusing to me for two reasons:

So why did Jesus curse the fig tree simply because He was hungry and could not find fruit on it?

And why did the tree wither from the inside out — from its roots?

The more I studied this passage, especially in the context of what was happening around Jesus at that time, the more it began to make sense.

The Structure in Mark Reveals Something Deepe

The Gospel of Mark presents these events in a very intentional structure.

First comes the fig tree.

Mark 11:12–14 — Jesus Curses the Fig Tree

And on the morrow, when they were come from Bethany, he was hungry:

And seeing a fig tree afar off having leaves, he came, if haply he might find any thing thereon: and when he came to it, he found nothing but leaves; for the time of figs was not yet.

And Jesus answered and said unto it,

‘No man eat fruit of thee hereafter for ever.’

And his disciples heard it
.”

Jesus approaches the tree expecting fruit because the tree is covered in leaves. Yet when He examines it closely, He finds nothing but outward appearance.

No fruit.

Then immediately after this comes another event.

Mark 11:15–17 — Jesus Cleanses the Temple

“And they come to Jerusalem: and Jesus went into the temple, and began to cast out them that sold and bought in the temple…”

And He taught, saying unto them,

"Is it not written, My house shall be called of all nations the house of prayer? but ye have made it a den of thieves.

This suddenly begins to mirror the fig tree.

Outwardly, the temple appeared holy:

From the outside, it looked spiritually alive and fruitful.

But inwardly, Jesus found corruption:

Just like the fig tree, the temple had leaves — but no fruit.

Mark 11:20–21 — The Fig Tree Withers

The next morning the disciples pass by again.

And in the morning, as they passed by, they saw the fig tree dried up from the roots.

And Peter calling to remembrance saith unto him,

‘Master, behold, the fig tree which thou cursedst is withered away.
’"

This is what ties the entire passage together.

The fig tree was not merely about hunger.

It was a living parable.

Why the Leaves Matter

When studying fig trees more closely, something fascinating appears.

Unlike many trees, fig trees begin producing small fruit buds before or together with the leaves. A healthy fig tree showing full leaves would normally also show signs of fruit.

The leaves were advertising fruitfulness.

But inwardly, the tree was barren.

This is the heart of the message.

Jesus was not judging the outward leaves.

He was searching for fruit.

The Temple Was the Same

The temple looked holy outwardly, but inwardly it lacked:

Even many who came there were focused more on buying and selling than on drawing near to God.

The leaves were there.

But the fruit was missing.

Withered From the Roots

One of the deepest parts of this passage is that the fig tree withered from its roots.

This points to something profound:
corruption always begins inwardly before it becomes visible outwardly.

A tree dies from the roots first.

The same was happening spiritually within the temple system.

And within around forty years, the Jerusalem temple itself would be destroyed by the Romans in 70 AD.

Who are we rooted to?

In essence, the deeper question becomes:

Who are we rooted in?

Who — or what — is truly the source of our life?

Because if the root is not grounded in God, the fruit produced from that life will eventually become corrupted.

A tree can appear healthy outwardly for a season, but if the roots are diseased, the fruit will eventually reveal it.

Jesus was constantly pointing people beyond outward appearances and toward the condition of the heart — the hidden root from which all fruit grows.

How This Relates to Us

This passage is not merely about an ancient fig tree or the temple.

It is also about us.

There are many people who appear righteous outwardly:

But inwardly their hearts may still be filled with:

Jesus consistently taught that God looks beyond appearances and examines the heart.

This is why Jesus says:

A tree is known by its fruit.

God is not searching merely for leaves of religion.

He is searching for the fruit that comes from a transformed heart.

True Fruitfulness

Jesus is not asking us merely to appear holy.

He is calling us to become holy inwardly:

When the roots are healthy, fruit naturally appears.

But when the roots are corrupted, eventually the entire tree withers.

The warning of the fig tree is ultimately this:

Outward appearance cannot replace inward transformation.

And that is what Jesus was truly seeking.

Lord, Lord: The Difference Between Knowing About Jesus and Truly Following Him

There are few sayings of Jesus more unsettling than this:

Not everyone who says to me, ‘Lord, Lord,’ will enter the kingdom of heaven…”

At first glance, these words feel almost harsh.
Because Jesus was not speaking to unbelievers here.
He was speaking to people who believed they knew Him.

People who used the right language.
People who appeared spiritual.
People who even did religious works in His name.

And yet He says something deeply sobering:

"I never knew you."

Not:
"I knew you once, and you abandoned me."

But:
"We never truly walked together."

The Danger of Knowing About Jesus

In today's world, it is easier than ever to know about Jesus.

We can listen to sermons endlessly.
Quore scripture
Debate theology
Post verses online
Attend church weekly
Even build an identify around Christianity

But Jesus repeatedly points toward something deeper than information.

He points towards transformation.

The fighting reality is this:
A person can become familiar with the language of faith while remaining distant from the heart of God.

A person can know scripture...
yet, still be ruled by prode.

Know worship songs...
yet, still nurture hatred

Know doctrine…
yet, refuse humility, forgiveness, repentance, or truth.

This is why Jesus constantly addressed the condition of the heart rather than outward appearance alone.

Following Jesus Was Never Meant to Be Performance

One of the great misunderstandings of faith is believing that Christianity is primarily about appearing righteous.

Jesus consistently challenged this idea.

The religious leaders of his time looked holy outwardly.
They knew the scripture better than anyone,
they prayed publicly,
and they followed the rituals carefully.

Yet many of them completely missed the heart of God standing right in front of them.

Why?

Because outward performance can exist without inward surrender.

A person can learn to act spiritually while remaining internally unchanged.

And this is where the warning of "Lod, Lord" becomes deeply personal.

Because the question is no longer 
"Do I identify as a Christian?"

The question becomes:
"Am I actually becoming aligned with the character of Christ?"

What Does It Mean to Truly Follow Him?

Following Jesus is not perfection.

If that were the case, none of us would stand.

The disciples themselves struggle constantly with fear, pride, doubt, anger, and selfishness.

Peter denies Jesus.
Thomas doubted.
The disciples argue over the status of importance

Yet Jesus continues walking with them.

Why?

Because, despite their failures, their hearts were still moving towards Him.

True faith is not sinless perfection.
It is ongoing alignment.

It is the willingness to:

The danger is not struggling

The danger is becoming comfortable with separation from God while convincing ourselves we are spiritually alive.

The Quiet Drift

Most people do not suddenly abandon truth overnight.

Drift happpens slowly

A little comparison here
A little bitterness there
A hidden resentment.
A tolerated addiction.
An unchecked ego.
A growing hypocracy

Over time, a person can maintain the appearance of faith while internally becoming distant from God

This is why Jesus warned people so strongly, not to condemn them, but because he understood how easily the human heart deceives itself.

We often judge ourselves by our intentions, while God looks at what we are becoming.

Relationship, Not Mere Recognition

One of the deepest parts of Jesus ' statement is the word "KNEW".

Basically,  to "know" someone is a deeply relational. It is closeness, walking together, communion, trust, and transformation through relationship.

This means salvation is not meant to be an intellectual agreement, but a living relationship with God that gradually changes who we are.

A tree is known by its fruit  - and eventually, what lives inside us becomes visible outside us. 

The Hope Inside the Warning

Yet, this teaching is not hopeless but contains mercy.

Jesus continually welcomed broken people who came honestly to Him

The prostitute
The tax collector
The doubter
The failure
The outcast

The people Jesus struggled with most were often not the broken, the spiritually proud, the ones convinces they already saw clearly.

God is not seeking the flawless, he is seeking hearts wiling to surrender, change, and walk with Him

The Question worth asking

Perhaps the most important question we can ask ourselves is this

Am I merely speaking the language of faith... Or am I actually becoming aligned with the heart of God?

Because in the end,  
Knowing about Jesus and trully following are not always the same thing

A Rich Man Had a Manager: A Parable About Urgency, Wisdom, and Eternity

This is a parable that Jesus told his disciples which could be found in Luke 16:1-9. It appears immediately after the parable of the Prodigal Son.
While the Prodigal Son focuses on mercy, this parable shifts the focus to responsibility. It is one of the most misunderstood parables in the Gospels as it sounds like Jesus is praising cunningness.

Jesus told His disciples:

“There was a rich man who had a manager, and charges were brought to him that this manager was squandering his property.

So he summoned the manager and said, ‘What is this I hear about you? Give an account of your management, because you cannot be my manager any longer.’

Realizing he was about to lose his position, the manager acted quickly. He called in the rich man’s debtors and reduced what they owed, hoping that when he lost his job, these people would welcome him into their homes.

Surprisingly, the master praised the manager — not for dishonesty, but for his shrewdness.

Jesus concluded:

“For the children of this world are more shrewd in dealing with their own generation than are the children of light.”

The Setting and Context
Jesus told this parable to his disciples and not outsiders, this matters as this is about how to live wisely in the light of what is coming.

At this point in Luke’s Gospel, Jesus is increasingly speaking about

  • accountability
  • stewardship
  • the coming Kingdom of God

Understanding the Parable
At first glance, this story feels uncomfortable.

Why would Jesus praise a dishonest manager

But notice carefully — Jesus never praises the dishonesty.

He praises something else.

The Characters

  • The Rich Man – Represents God — the true owner of everything.
  • The Manager (Steward) – Represents us – entrusted with time, resources, influence, and opportunity
  • The Debt Reduction – Represents using present resources to prepare for the future

The manager suddenly realizes that his time is short , his position is ending and he needs to act now. This realization changes everything.

What Jesus Is Really Teaching
Jesus is making a contrast

“If people are this intentional about securing their future in this world how much more intentional should they be about eternity”

The parable is not about cheating systems, but about urgency. We live as stewards of time, money, relationships, opportunities and one day the stewardship will and all this will be lost.

Jesus message is simple and piercing

Use what you have now for what will last forever

The Core Message
The parable teaches us that – Life is temporary, Opportunity is limited, and Wisdom is to act before it is too late.

Faith is not passive waiting , it is intentional living

Reflection for Today

Most of us are very good art planning careers, finances, or retirements but Jesus gently asks

Are you equally intentional about your soul ?

Not our of fear but out of wisdom

This is a call , an invite for us to live “awake”, not “asleep”

Using Theme to supercharge React Projects : A Quick Guide

Let’s talk theme files—the secret sauce to keep your React project’s styling clean, scalable, and, well, just plain fun. Imagine this: you’re neck-deep in a React app, and your styling is scattered across components. One tweak to your color palette, and you’re knee-deep in dozens of CSS changes. Enter the magic of theme files. They not only save you from this chaos but supercharge your workflow with flexibility, consistency, and ease.

But why should you care about using a theme file for styling in React? Here’s the thing: it’s not just about keeping your code neat—it’s about creating a scalable system where tweaking one line in a theme file can change the look of your entire app. It’s like being the architect of a universe, where every component dances to the tune of your style variables.

Why Use a Theme File?

To be honest, building an app isn’t just about code; it’s about design. You want a sharp, clean interface. But what happens when your app grows and you need to make global changes? Hunting down individual styles is a nightmare. Enter the theme file—a single source of truth for all your styling needs.

Think of it as the control center for your app’s appearance. You define variables like colors, fonts, margins, and more in a centralized file. Want to update the entire app’s primary color? Change one variable. Done. It’s that simple.

Let’s build a tiny project that shows how to create and use a theme file to style React components. In this example, we’ll show how to define a theme file and use it to control the styling of your components like a boss.

  • Setting Up the Theme File

First, let’s create a theme file. In your project, make a theme.js file that looks something like this:

// theme.js
export const theme = {
  colors: {
    primary: '#3498db',
    secondary: '#2ecc71',
    background: '#f5f5f5',
    text: '#333333',
  },
  fontSizes: {
    small: '12px',
    medium: '16px',
    large: '24px',
  },
};

This theme file is your styling power station. You control everything from colors to fonts here.

  • Using the Theme in a Styled Component

Now, let’s use this theme in a React component using styled-components:

// MyButton.js
import styled from 'styled-components';
import { theme } from './theme';

const Button = styled.button`
  background-color: ${theme.colors.primary};
  color: ${theme.colors.text};
  padding: 10px 20px;
  font-size: ${theme.fontSizes.medium};
  border: none;
  border-radius: 5px;
  cursor: pointer;

  &:hover {
    background-color: ${theme.colors.secondary};
  }
`;

export default Button;

our button now responds to the colors and font sizes from the theme file. Want to make it a little edgier? Change the variables in your theme.js file and watch your app transform.

  • Using the Component

Finally, let’s use this component in your app:

// App.js
import React from 'react';
import Button from './MyButton';

function App() {
  return (
    <div style={{ backgroundColor: '#f5f5f5', padding: '20px' }}>
      <h1>Styled with Theme Files!</h1>
      <Button>Click Me</Button>
    </div>
  );
}

export default App;

Now, your app pulls its styling from the theme file. Update the theme, and everything updates like magic. Consistency? Check. Flexibility? You got it.

Final thoughts

So, why go through the hassle of using theme files? Because it’s not a hassle—it’s freedom. As your app scales, having a single place to manage your style variables makes development faster, more fun, and way less stressful. Plus, your app will thank you for the consistent, polished look.

Ready to try it out? Go ahead, give theme files a whirl, and watch your React project take styling to the next level!

Revamping Your Development Process: Elevating QA, UAT, and Production with Speed and Precision

In my experience working with or consulting engineering teams on supercharging their development, QA and release processes, I have seen that most places in trying to balance speed and quality has led to immense mental stress and sleepless nights to engineering teams. Not only that , in most cases you end up shipping half baked or crappy code to production.

You do need to ship features out the door quickly, but you also need to ensure that we follow a process that allows us to ship code safely. There should be very little “Oh shoot, this should have been caught on QA or UAT, not while the users are using and their customer is in front of them”. Here’s a deep dive into how you can sharpen your process while keeping the madness under control.

  • Branching Strategy: Keep It Clean, Keep It Smart

Let’s start with branching—a no-brainer, but often done wrong. If your DEV, QA, and UAT environments aren’t in sync, you’re playing with fire. Here’s the fix:

  1. DEV Branch: Think of it as the playground where things can break. Merge feature branches into DEV, but don’t push untested code past this point. DEV should be active but treated with caution.
  2. QA Branch: As soon as DEV has something worth testing, promote it to the QA branch. This branch is sacred ground for the QA team to test without distractions. Don’t cut corners by pushing to UAT or production before it’s stable.
  3. UAT Branch: The UAT branch is where your end users get involved. Ensure this branch is almost identical to production so that any last-minute issues can be caught and resolved before going live. If something goes wrong here, it’s the final warning before disaster.
    • Tools that can help:
      • Gitflow: A branching model that can make all this neat and manageable.
      • Feature Toggles (via LaunchDarkly or Unleash): Allows features to be toggled on/off without deploying new code, giving you more flexibility.

A good branching and merging strategy is imperative and is the first sanity check you need to have.

  • Fixing QA Issues in Real-Time and Keeping Everything in Sync

Now that we’ve got branching down, let’s tackle what happens when things go wrong in QA (because they will). Bugs found in QA should be fixed on the QA branch—don’t touch DEV yet. Once the fix is tested, backport it to DEV to ensure the same bug doesn’t creep back in later.

  • Fix in QATest in QAMerge to DEV. No shortcuts.

Skipping this will create chaos in UAT and production. You’ll end up with different bugs in each environment, making it impossible to keep track.

  • Speeding Up QA Without Cutting Corners

QA often feels like the bottleneck, right? The devs are done, and the product team is tapping their feet impatiently while QA painstakingly goes through tests. But we can supercharge QA without compromising thoroughness.

  1. Automate Smoke Tests: Tools like Selenium, Cypress, or Playwright can automate repetitive tests so that QA doesn’t have to spend time checking basic functionality every single time. Smoke tests should run immediately after every push to the QA branch, catching major issues early.
  2. Performance Testing Early: Use tools like JMeter or Gatling to test system performance during QA, not after. This helps identify bottlenecks before you’re staring down the barrel of a UAT disaster.
  3. Create and Maintain Robust Test Data: This is a big one. Testing with half-baked or outdated data isn’t going to cut it. Use data anonymization tools like Tonic.ai or Mockaroo to generate realistic datasets that mirror production.
  4. Shift-Left Testing: QA shouldn’t be an afterthought. Developers should write unit tests for their code (think Jest, Mocha, or JUnit), and the pipeline should include integration tests (with TestCafe or Pact for API testing) early in the process.
  • Shipping to UAT Without Losing Your Mind

You’ve got stable code from QA, and it’s time to ship it to UAT. This is where customers or users do their validation, but it has to be done methodically.

  1. Mirror Production: UAT should reflect production as closely as possible. Tools like Docker or Kubernetes can help create isolated environments that are clones of production, allowing your team to test under real-world conditions.
  2. Automated Deployments: Use tools like Jenkins, CircleCI, or GitLab CI/CD to automate deployments into UAT, making the process faster, repeatable, and less error-prone.
  3. Quick Feedback Cycles: Don’t leave customers hanging with long UAT cycles. Use tools like TestRail or Zephyr to manage test cases and feedback in UAT. Make sure there’s a clear process for logging bugs and responding quickly.
  • UAT Expediency: Customers Love Speed—Give It To Them

UAT often drags because end-users aren’t testing efficiently. The key here is to give them structure and ensure they’re not just clicking around randomly:

  1. Test Case Templates: Provide your customers with a checklist or clear test cases to follow. Use tools like Cucumber for behavior-driven development (BDD), where test scenarios are written in plain English, making it easy for non-technical users to understand what they should be testing.
  2. Run Sanity Checks in Parallel: Before customers even touch the UAT environment, run automated sanity checks to ensure the system is functioning correctly (similar to the smoke tests in QA).
  3. Feedback Mechanism: Use tools like Trello or JIRA to collect user feedback quickly and efficiently. Implement a tagging system that can help prioritize critical issues versus nice-to-haves, so the UAT process doesn’t drag on forever.

You can’t have your cake and eat it too—rushing things to production without proper QA and UAT will lead to headaches down the road. By creating a clear pipeline between DEV, QA, and UAT, and keeping those environments in sync, you’ll reduce the number of bugs creeping into production.

Key Tools Recap

  • Branching: Gitflow, LaunchDarkly (feature toggles)
  • Automation: Selenium, Cypress, JMeter, Jenkins, Docker
  • Data Preparation: Tonic.ai, Mockaroo
  • Test Management: TestRail, Zephyr
  • Feedback: Trello, JIRA

Focus on testing the right way with proper tools and processes, and you’ll see smoother releases, faster UAT cycles, and a lot less hair-pulling come production time.

Introduction to most commonly used React Hooks

React Hooks are functions that allow you to use state and other React features without writing a class. Introduced in React 16.8, they provide a powerful and simpler way to manage state, handle side effects, and reuse logic across components.

In this article, we’ll dive into the most commonly used hooks such as : useState, useEffect, useContext, useReducer, and useRef. We’ll also walk through how to create a custom hook with an example for managing form data, which is useful when sending data to an API.

  • useState – Managing State in Functional Components
    • The useState hook is the simplest way to manage state in a functional component. It allows you to add state variables to your component, and its syntax is straightforward:
import React, { useState } from 'react';

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

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  );
}

In this example, useState initializes count to 0, and setCount is used to update the state. Each time the button is clicked, the count increases by 1.

  • useEffect – Handling Side Effects
    • useEffect allows you to perform side effects in your components, such as fetching data, updating the DOM, or setting up subscriptions.
    • useEffect allows mainly the following life cycle methods to be achieved
      • componentDidUpdate
      • componentDidMount
      • componentWillUnmount
import React, { useState, useEffect } from 'react';

function Timer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setSeconds(prevSeconds => prevSeconds + 1);
    }, 1000);

    return () => clearInterval(interval); // Cleanup on component unmount
  }, []);

  return <div>Timer: {seconds} seconds</div>;
}

Here, useEffect sets up a timer that updates every second. The cleanup function inside useEffect ensures that the interval is cleared when the component unmounts.

  • useContext – Sharing State Between Components
    • useContext is used to share state across components without having to pass props manually at every level. It’s often used with the React Context API.
import React, { useContext } from 'react';

const ThemeContext = React.createContext('light');

function ThemedButton() {
  const theme = useContext(ThemeContext);

  return <button className={theme}>I'm a {theme} themed button</button>;
}

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <ThemedButton />
    </ThemeContext.Provider>
  );
}

In this example, ThemedButton uses useContext to consume the theme value from ThemeContext. The theme is provided by the ThemeContext.Provider in the App component.

  • useReducer – Complex State Logic
    • useReducer is useful when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one.
import React, { useReducer } from 'react';

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
    </div>
  );
}

Here, useReducer is used to manage the count state with more complex logic than useState. The reducer function handles actions to update the state

  • useRef – Accessing DOM Elements and Persisting Values
    • useRef provides a way to access DOM elements directly or persist values across renders without causing re-renders.
import React, { useRef } from 'react';

function TextInputWithFocusButton() {
  const inputRef = useRef(null);

  const handleClick = () => {
    inputRef.current.focus();
  };

  return (
    <div>
      <input ref={inputRef} type="text" />
      <button onClick={handleClick}>Focus the input</button>
    </div>
  );
}

In this example, useRef is used to create a reference to the input element, allowing us to focus it when the button is clicked.

  • Custom Hook for Form Data
    • Custom hooks allow you to extract and reuse logic across multiple components. Let’s create a custom hook to manage form data, which we’ll send to an API.
import { useState } from 'react';

function useFormData(initialValues) {
  const [formData, setFormData] = useState(initialValues);

  const handleChange = (event) => {
    const { name, value } = event.target;
    setFormData(prevData => ({
      ...prevData,
      [name]: value
    }));
  };

  const resetForm = () => {
    setFormData(initialValues);
  };

  return { formData, handleChange, resetForm };
}

function ContactForm() {
  const { formData, handleChange, resetForm } = useFormData({ name: '', email: '' });

  const handleSubmit = async (event) => {
    event.preventDefault();
    // Here, you would typically send formData to an API
    console.log(formData);
    resetForm();
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Name:
        <input type="text" name="name" value={formData.name} onChange={handleChange} />
      </label>
      <br />
      <label>
        Email:
        <input type="email" name="email" value={formData.email} onChange={handleChange} />
      </label>
      <br />
      <button type="submit">Submit</button>
    </form>
  );
}

This custom hook useFormData manages form state, handling input changes and resetting the form. It’s reusable and keeps your component logic clean.

React Hooks provide a powerful and intuitive way to manage state, handle side effects, and encapsulate reusable logic in your components. By understanding and using hooks like useState, useEffect, useContext, useReducer, and useRef, you can write more concise and maintainable code.

Git strategy for different projects

Different organization have their own software products and engineering practices to support the development. Having a good repository management is key to having a great engineering process. However, in my experience in consulting organizations in digitalizing and adopting robust engineering practices, I have seen teams struggling to find the right repository management practice for their project or product structure. In this article I will try to provide recommended git practices for four common types of agile projects: internal projects with multiple teams, external client projects, multi-client products with different versions, and SaaS products with a single codebase used by multiple clients.

  • Internal Project with Multiple Development Teams
    • Project Characteristics:
      • Client: Single internal client.
      • Teams: Multiple development teams.
      • Release Strategy: Planned production releases, QA releases every sprint.
    • Recommended Git Practices:
      • Branching Strategy:
        • Main Branch: Use 'main' or master‘ as the stable branch where the latest production-ready code resides.
        • Development Branch: Create a ‘develop‘ branch where all new features, improvements, and bug fixes are merged before they are considered for production.
        • Feature Branches: Each team should work on feature branches (‘feature/feature-name‘) derived from the ‘develop‘ branch. This isolates feature development and minimizes integration conflicts.
        • Release Branches: Before each planned release, create a ‘release/release-version‘ branch from ‘develop‘. This branch is used for final QA, minor fixes, and preparing the code for production.
      • Merging Strategy:
        • Feature branches should be merged into ‘develop‘ via pull requests (PRs). This ensures code reviews and testing are part of the process.
        • Once a release branch is created, only critical bug fixes should be merged into it, usually via hotfix branches (‘hotfix/hotfix-name‘) if necessary.
        • After the production release, the ‘release‘ branch should be merged into both main and develop to ensure all changes are captured.
      • Releasing Strategy:
        • Schedule regular merges from the ‘release‘ branch into ‘main‘ to mark official production releases.
        • Use Git tags to mark specific releases (v1.0.0, v1.1.0) on the ‘main‘ branch for easy reference and rollback if needed.
  • main: Stable, production-ready code.
  • develop: Integration branch for all features.
  • feature/feature-x: Individual branches for each feature.
  • release/release-version: Preparation for release, includes QA and final bug fixes.
  • hotfix/hotfix-name: For urgent fixes that need to be applied after a release.
  • External Client Project Developed by One Team
    • Project Characteristics:
      • Client: Single external client.
      • Teams: One development team.
      • Release Strategy: Immediate releases after QA completion and critical bug resolution.
    • Recommended Git Practices:
      • Branching Strategy:
        • Main Branch: The ‘main‘ branch should be kept production-ready at all times, as releases are frequent and driven by immediate needs.
        • Feature Branches: Use ‘feature/feature-name‘ branches for each new feature or bug fix.
        • Hotfix Branches: Given the nature of immediate releases, hotfix branches (‘hotfix/hotfix-name‘) should be used for critical issues identified after a release.
      • Merging Strategy:
        • Merge completed feature branches directly into ‘main‘ via PRs after thorough QA. The simplicity of the project structure allows for this direct approach.
        • Hotfix branches should be merged into ‘main‘ immediately after the issue is resolved and tested.
      • Releasing Strategy:
        • Releases are tagged directly on the main branch as soon as they pass QA (v1.0.1, v1.0.2).
        • Consider using lightweight tags for interim builds or QA releases, and annotated tags for official client releases.
  • main: Production-ready, frequently updated branch.
  • feature/feature-x: Feature development branches.
  • hotfix/hotfix-name: Branches created to fix critical issues immediately after a release.
  • Multi-Client Product with Different Versions
    • Project Characteristics:
      • Clients: Multiple clients on different versions of the product.
      • Teams: Several teams working on various product areas.
      • Release Strategy: Versioned releases tailored to individual clients.
    • Recommended Git Practices:
      • Branching Strategy:
        • Main Branch: Use the ‘main‘ branch as the latest stable version of the product.
        • Version Branches: Maintain long-lived branches for each major version (‘release/1.x, release/2.x‘). These branches allow you to backport fixes or enhancements to clients on older versions.
        • Feature Branches: Each new feature or improvement should be developed in a feature branch (‘feature/feature-name‘) from the target version branch.
      • Merging Strategy:
        • Features are merged into the respective version branch via PRs. If a feature is applicable to multiple versions, cherry-pick the commits across the relevant branches.
        • Hotfixes should be merged into the version branch and, if necessary, forward-ported to newer versions.
      • Releasing Strategy:
        • Tag releases on the appropriate version branch (v1.5.0, v2.3.0) before deploying to clients.
        • Use tags and branches to manage client-specific customizations, if necessary, by creating branches like ‘client/client-name‘ from the relevant version branch.
  • main: Latest stable version of the product.
  • release/1.x, release/2.x: Long-lived branches for different versions of the product.
  • feature/feature-x: Features developed for specific versions.
  • hotfix/hotfix-name: Hotfixes applied to specific versions.
  • SaaS Product with a Single Codebase for Multiple Clients
  • Project Characteristics:
    • Clients: Multiple clients using the same codebase.
    • Teams: Multiple teams contributing to the product.
    • Release Strategy: Continuous deployment with feature toggles or configurations for client-specific customizations.
  • Recommended Git Practices:
    • Branching Strategy:
      • Main Branch: Use ‘main‘ for the latest production-ready code. Given the continuous deployment model, the main branch should always be stable and ready for release.
      • Feature Branches: Develop features in isolated ‘feature/feature-name‘ branches, using feature toggles or flags to manage client-specific functionality.
      • Environment Branches: Optionally, use branches like ‘staging‘ or ‘pre-production‘ for testing in environments that mimic production.
    • Merging Strategy:
      • Merge feature branches into main after testing and code review, ensuring all feature toggles or client configurations are correctly implemented.
      • Use environment branches for additional testing stages if your deployment pipeline requires it.
    • Releasing Strategy:
      • Deploy directly from the ‘main‘ branch using automated CI/CD pipelines.
      • Tag major releases for easier rollback (v3.0.0, v3.1.0), and use feature flags to manage rollout across different clients.
  • main: Single codebase ready for production deployment.
  • feature/feature-x: Branches for feature development, often managed with feature toggles.
  • staging: Optional branch for additional testing before production deployment.

General Git Best Practices

  • Commit Often and Meaningfully: Make frequent, small commits with descriptive messages to improve traceability and collaboration.
  • Use Pull Requests: Even for small teams, PRs encourage code reviews and discussion, leading to higher code quality.
  • Rebase with Caution: While rebasing keeps a linear commit history, avoid it on public branches to prevent conflicts.
  • Automate Testing: Integrate automated tests into your Git workflow to catch issues early and maintain code quality.
  • Documentation: Maintain clear documentation on your branching and release strategy to ensure all team members follow the same practices.

Well I hope above gives teams trying set up a repository management strategy to match their project / product, some guidelines.

Advance Javascript : Lazy Loading

In my previous articles I explained optimization patterns such as deboucing and throttling, in this article let’s do a deep dive into lazy loading , which defers the loading of resources until they are actually needed. This article will walk you through a simple example of implementing lazy loading in a TypeScript web application.

What is Lazy Loading?

Lazy loading is a design pattern commonly used to delay the initialization of objects until the point at which they are needed. In the context of web development, this often refers to loading resources like images, scripts, or modules only when they are required, rather than at the start of the application. This can significantly reduce initial load times and improve overall performance.

Project Overview

We will create a web page with a button. When the button is clicked, a module containing a heavy computation function will be loaded and executed. This example uses TypeScript and native ES modules to achieve lazy loading.

Setting Up the Project

  • Project Structure
lazy-loading-example/
├── dist/
│   ├── index.html
│   ├── app.js
│   └── lazyModule.js
├── src/
│   ├── index.html
│   ├── app.ts
│   └── lazyModule.ts
├── tsconfig.json
└── package.json
  • Initialize the Project
npm install typescript --save-dev
npx tsc --init
  • Configure TypeScript

Edit ‘tsconfig.json ‘to target ES6 and use ESNext modules:

{
  "compilerOptions": {
    "target": "es5",
    "module": "esnext",
    "outDir": "./dist",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"]
}

Writing the Code

  • Create HTML File

Create ‘src/index.html‘ with a button and script tag

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Lazy Loading Example</title>
</head>
<body>
  <h1>Lazy Loading Example</h1>
  <button id="loadButton">Load Module</button>
  <script type="module" src="./app.js"></script>
</body>
</html>
  • Create Lazy Module

Create ‘src/lazyModule.ts‘ which contains a heavy task function:

export const performHeavyTask = () => {
  console.log('Performing a heavy task...');
  // Simulate a heavy task
  for (let i = 0; i < 1e7; i++) {}
  console.log('Heavy task completed.');
};
  • Create Main Application File

Create ‘src/app.ts‘ to handle button click and load the module

const loadButton = document.getElementById('loadButton') as HTMLButtonElement;

loadButton.addEventListener('click', async () => {
  const module = await import('./lazyModule.js');
  module.performHeavyTask();
});

Compile and Run

  • Compile the TypeScript files
npx tsc
  • Serve the Application

Use a static file server to serve the files. Here’s a quick way to do it using ‘http-server

npm install -g http-server
http-server dist
  • Open in Browser

Open your browser and navigate to http://localhost:8080 (or the port http-server provides). Click the “Load Module” button to see the lazy loading in action. The performHeavyTask function from lazyModule will be dynamically imported and executed, demonstrating lazy loading.

Conclusion

Lazy loading is an effective way to optimize web application performance by loading resources only when they are needed. In this example, we’ve seen how to implement lazy loading in a TypeScript web application using native ES modules. This technique can be extended to other resources like images and scripts to further enhance your application’s performance.

By adopting lazy loading, you can ensure that your web applications are both efficient and responsive, providing a better experience for your users

For complete source – https://github.com/IndikaMaligaspe/advance-javascrips/tree/master/topics/performance-optimization

Advance Javascript: Throttling

In my previous article I went through a performance optimization strategy called debouncing, in this article let’s go through an effective technique to manage the frequency of function execution – throttling. We’ll explore what throttling is, why it’s useful, and how to implement it with examples from both a console application and a web application using TypeScript.

What is Throttling?

Throttling is a technique used to ensure that a function is executed at most once in a specified period. This is particularly useful in scenarios where an event fires multiple times in quick succession, like keystrokes, window resizing, or scroll events. By throttling, we can control the execution frequency of a function, improving performance and user experience.

Why Use Throttling?

Imagine a scenario where you have a function that updates the UI based on user scroll events. Without throttling, the function could be called numerous times per second, potentially degrading performance. Throttling ensures that the function is called at most once every specified period, reducing the workload and improving efficiency.

Throttling in a Console Application

Let’s start with a simple example in a console application using Node.js. We’ll create a throttled function that logs a message only once every 2 seconds while the user types.

TypeScript Example:

import readline from 'readline';

type Throttle = <T extends(...args:any[]) => void> (func:T, limit: number) => (...args: Parameters<T>) => void;

const throttle : Throttle = (func, limit) => {
    let lastFunc: NodeJS.Timeout | undefined;
    let lastRan: number;

    return function (this: unknown, ...args: Parameters<typeof func>){
        const context = this;
        if(!lastRan) {
            func.apply(context, args);
            lastRan = Date.now();
        } else {
            if(lastFunc) clearTimeout(lastFunc);
            lastFunc = setTimeout(() => {
             if((Date.now() - lastRan >= limit)){
                func.apply(context, args);
                lastRan = Date.now();
             }
            }, limit);
        }
    };
}

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

const handleKeystroke = throttle((input: string) => {
    console.log('Keystroke is :', input.trim())
}, 2000);

console.log(' Start Typing :');
rl.on('line', handleKeystroke)

In this example, the handleKeystroke function logs the input at most once every 2 seconds, regardless of how fast you type and press Enter.

Throttling in a Web Application

Now, let’s look at an example in a web application. We’ll throttle a function that handles input events on a search field, ensuring the search function is only called at most once every 300 milliseconds.

HTML:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Throttling Example</title>
</head>
<body>
  <input type="text" id="searchInput" placeholder="Type to search..." />
  <script src="app.js"></script>
</body>
</html>

TypeScript (app.ts):

First, install TypeScript and configure it if you haven’t already

npm install typescript --save-dev
npx tsc --init

Now, here’s the TypeScript code for the web application

const throttle = <T extends (...args: any[]) => void>(func: T, limit: number) => {
  let lastFunc: ReturnType<typeof setTimeout>;
  let lastRan: number | undefined;
  return function (this: unknown, ...args: Parameters<T>) {
    const context = this;
    if (!lastRan) {
      func.apply(context, args);
      lastRan = Date.now();
    } else {
      if (lastFunc) clearTimeout(lastFunc);
      lastFunc = setTimeout(() => {
        if ((Date.now() - lastRan!) >= limit) {
          func.apply(context, args);
          lastRan = Date.now();
        }
      }, limit - (Date.now() - lastRan!));
    }
  };
};

const handleInput = throttle((event: Event) => {
  const target = event.target as HTMLInputElement;
  console.log('Search query:', target.value);
}, 300);

const searchInput = document.getElementById('searchInput') as HTMLInputElement;
searchInput.addEventListener('input', handleInput);

Explanation

  • Type Declaration
    • Throttle‘: A type for the throttle function which ensures the input function func is only called once every limit milliseconds.
  • Throttle Function
    • throttle‘ takes a function func and a limit limit in milliseconds.
    • Uses ‘lastFunc‘ to store the timeout ID and ‘lastRan‘ to store the timestamp of the last execution.
    • Checks if ‘lastRan‘ is undefined (first run) and executes ‘func‘.
    • If not the first run, clears any previous timeout and sets a new timeout to execute func after the remaining time until ‘limit‘ has passed
  • HTML and TypeScript Integration
    • The input field in the HTML triggers the throttled ‘handleInput‘ function
    • The ‘handleInput‘ function logs the search query at most once every 300 milliseconds

Conclusion

Throttling is a powerful technique to control the execution frequency of functions in JavaScript, ensuring they are called at most once in a specified period. By applying throttling in both console and web applications, you can significantly improve performance and user experience. Whether you’re managing user input, scroll events, or resize events, throttling ensures that your functions are executed efficiently and only when necessary.

For complete source – https://github.com/IndikaMaligaspe/advance-javascrips/tree/master/topics/performance-optimization