When '===' Tells You Something You Don't Want to Hear

## Context

We are building a UCC (Uniform Commercial Code) filing search and monitoring system for an enterprise customer. Think of it as a specialized search engine for legal filings. Businesses use it to track liens, verify creditor positions, and monitor changes to secured transactions.

Our stack:
- Backend: Node.js, Express, TypeScript
- Database: PostgreSQL
- ORM: Drizzle
- Driver: node-postgres (`pg`)

This is a story about a bug that passed code review, worked in development, and silently failed in staging. The cause was not bad logic. It was an assumption we did not know we were making.

## The Bug

We built a feature that displays party changes in a filing's timeline. It worked locally. Tests passed. But after deploying to staging, the data simply did not appear.

No errors. No warnings. Just an empty list where names should have been.

We eventually traced it to this line:

```ts
.filter((p) => p.amendment_filing_id === amendment.id)
```

The quick fix that made everything work again was this:

```ts
.filter((p) => Number(p.amendment_filing_id) === amendment.id)
```

That unblocked us, but it also made us uneasy. And it should have.

## What Was Actually Happening

We had two different query paths touching the same database column.

1. A Drizzle ORM query returned `amendment.id` as a number.
2. A raw SQL query using `pg` returned `amendment_filing_id` as a string.

Both values looked identical when logged:

```ts
53703
"53703"
```

But strict equality compares both value and type:

```ts
"53703" === 53703 // false
```

So the filter silently returned an empty array every time.

Nothing crashed. JavaScript just moved on.

## Try It Yourself

You can reproduce this behavior in the [TypeScript Playground](https://www.typescriptlang.org/play):

```ts
const ormResults = [
{ id: 53703, name: "Amendment A" },
];

const rawSqlResults = [
{ amendment_id: "53703", party: "Acme Corp" },
];

const broken = ormResults.map(a => ({
...a,
parties: rawSqlResults.filter(p => p.amendment_id === a.id),
}));

const fixed = ormResults.map(a => ({
...a,
parties: rawSqlResults.filter(p => Number(p.amendment_id) === a.id),
}));

console.log(broken); // empty parties
console.log(fixed); // works
```

The code is correct. The comparison is strict. The data shapes are not.

## Why PostgreSQL Returns Strings

This behavior is intentional.

PostgreSQL `bigint` supports values far larger than JavaScript can safely represent as a Number. To avoid silent precision loss, `pg` returns bigint columns as strings.

The ORM did not cause the bug. The problem was letting the same identifier exist in two different runtime shapes depending on how it was queried.

## Why This Slipped Through

This bug did not suddenly appear in staging. It was always there.

We missed it because:
- Our dev database did not exercise this code path
- Empty data looked like "no changes", which felt valid
- There were no runtime errors
- The code looked obviously correct

Strict equality did not betray us. It told us something we did not want to hear.

## The Real Fix

The long term fix was not "use `Number()`. The fix was consistency.

We removed the raw SQL path and routed everything through the ORM so both queries flowed through the same schema and type configuration. Once the identifier had a single runtime representation, the comparison became boring again:

```ts
.filter((p) => p.amendmentFilingId === amendment.id)
```

No coercion. No surprises.

## Lessons Learned

- Strict equality is not the enemy. It exposes disagreement.
- Mixing ORM queries and raw SQL without aligning types is risky.
- When identifiers leave the database, their type can shift unexpectedly.
- Fix type mismatches at boundaries, not inside comparisons.
- Be suspicious of silent empty results.

Looking back, the quick fix solved the immediate problem. The real fix made the system harder to break in the future.

Time to find the bug: longer than we want to admit.

Time to fix it once we understood it: minutes.

Relief that it failed in staging and not production: considerable.