Daily Dev Post logo
Daily Dev Post
Published on, Time to read
🕒 6 min read

A Bug Caused by a Wrong Assumption I Made

A Bug Caused by a Wrong Assumption I Made

This bug did not come from a complex algorithm.
It did not come from a race condition I barely understood.
It did not come from some edge case.

It came from a wrong assumption.

And what made it worse is that the assumption felt reasonable.
It felt safe.
It felt obvious.

Which is why I didn’t question it.

The App Was “Working”

At the time, I was working on a fairly normal frontend feature.

Nothing fancy.

A page that:

  • Fetches data from an API
  • Displays a list
  • Allows the user to interact with it
  • Updates the UI based on state

The kind of thing I had built many times before.

The app worked.
The UI looked correct.
There were no errors in the console.
No crashes.
No warnings.

QA tested it.
Product was happy.
I moved on.

That’s usually how bugs begin.

Not with failure.
But with false confidence.

The Assumption I Made

Here was my assumption:

“This data will only change when the user explicitly triggers an action.”

So I wrote my code around that idea.

I assumed:

  • The API response shape would stay consistent
  • The data would not update in the background
  • State changes would only happen in one place
  • A component would render once per user action

Because that’s how it worked during development.

And that’s a dangerous sentence.

How the Assumption Shaped the Code

Because of this assumption, I did a few things that felt harmless at the time.

Derived state from props (once)

const [items, setItems] = useState(props.items)

Skipped dependencies in useEffect

useEffect(() => {
  fetchData()
}, [])

Memoized aggressively

const processedItems = useMemo(() => {
  return items.filter(item => item.active)
}, [])

Everything looked clean.
Short.
Readable.

I remember thinking:

“Nice. This is simple.”

It was simple because it was incomplete.

The Bug Appeared Later

Weeks later, someone reported a strange issue.

Not always reproducible.
Not happening for every user.
Not crashing the app.

Just… wrong behavior.

Sometimes:

  • The list showed outdated data
  • A button stopped responding correctly
  • A value on the screen didn’t match the backend

Refreshing the page fixed it.

That made it worse.

Because now it felt like:

  • Maybe a network issue
  • Maybe caching
  • Maybe user error

Classic excuses.

My First Reaction

My first reaction was not curiosity.

It was defense.

“I didn’t change anything there.”
“That code is stable.”
“It’s been live for weeks.”

This is another quiet mistake we make.

We trust time instead of logic.

Just because code has existed without obvious failure does not mean it’s correct.

Digging Into the Problem

Eventually, I sat down and really looked at it.

I added logs.
I slowed things down.
I tried to reproduce it locally.

Slowly, a pattern appeared.

The data was changing.
Just not when I expected it to.

There was:

  • A background refetch
  • A shared store update
  • Another component mutating the source

The data changed without user interaction.

And my component did not react to it.

Because I told React:

“Don’t worry. This never changes.”

That was the assumption.

The Exact Moment It Clicked

I stared again at this line:

const [items, setItems] = useState(props.items)

And asked myself:

“When does this update?”

The answer was simple.

Only once.

Because that’s how useState works.

I had assumed:

Props change → state updates automatically

But React never promised that.

That belief came from my own mental shortcut.

Why This Bug Was Dangerous

This bug wasn’t loud.

It didn’t:

  • Throw errors
  • Break builds
  • Fail tests

It quietly made the UI drift out of sync with reality.

Those are the worst bugs.

Users don’t report them clearly.
They just lose trust.

They think:

  • “Sometimes it works”
  • “Sometimes it doesn’t”

And eventually, they stop using the product.

The Real Root Cause

The bug wasn’t:

  • React
  • Hooks
  • The API
  • The backend

The real cause was this:

I designed the code around how I expected the system to behave, not how it actually behaved.

I optimized for a scenario that existed only in my head.

Fixing the Bug

The fix was not complex.

I:

  • Removed derived state where it wasn’t needed
  • Let props stay as props
  • Added real dependencies to effects (and stopped making this mistake with useEffect)
  • Allowed re-renders to happen naturally
const items = props.items

useEffect(() => {
  process(items)
}, [items])

The code became slightly longer.
Slightly less “clean”.

But much more honest.

And that mattered more.

What I Learned From This

1. Assumptions Are Invisible Bugs

They don’t show up in code reviews easily.

They show up as thoughts like:

  • “This will never change”
  • “We don’t need to handle that”
  • “It’s safe to ignore this case”

That’s where bugs grow.

2. Working Code Can Still Be Wrong

If the UI looks fine today, it doesn’t mean it will look fine tomorrow.

Especially when:

  • Data changes over time
  • Multiple parts affect the same state
  • The app grows

Silence is not proof of correctness.

3. Simplicity Can Be Misleading

Simple code is good.
But oversimplified assumptions are not.

Complexity doesn’t disappear just because you ignore it.

It waits.

4. React Does Exactly What You Tell It To

React didn’t betray me.

It followed my instructions perfectly.

I told it:

  • Initialize once
  • Don’t re-run
  • Trust this value

It trusted me.

That trust was misplaced.

How I Think About Code Now

Now, I ask different questions.

Not:

“Does this work right now?”

But:

  • “What am I assuming here?”
  • “What breaks if this changes?”
  • “What am I telling React to ignore?”

I care less about:

  • Fewer lines
  • Clever tricks
  • Early optimization

And more about:

  • Clear behavior
  • Honest dependencies
  • Predictable updates

A Small Mental Shift

One idea helped me a lot:

Treat assumptions as bugs that haven’t happened yet.

If your code relies on something never changing, write that down.

If it feels “obvious”, question it.

Because software rarely stays the way we imagine it.

Final Thought

This bug didn’t make me feel smart.

It made me feel humbled.

The mistake wasn’t technical.
It was human.

I assumed.
I trusted that assumption.
And I built on top of it.

Now, I still make assumptions.

But I try to notice them.

Because most bugs don’t come from what we don’t know.

They come from what we’re too sure about.

Did you like the article? Support me on Ko-Fi!

Pradip Jarhad

Pradip Jarhad

Software Developer

Hey, I’m Pradip. Software Developer and Creator of DailyDevPost. I write about React, JavaScript, debugging and frontend lessons I learn while building real projects. No theory heavy posts, just practical notes from daily development work.