Cosine Similarity is Dead. Long Live Cosine Similarity.
A practical breakdown of when this metric works for embeddings, when it fails, and what to use instead.

Let me tell you about my relationship with linear algebra.
Back in college, I sat through endless lectures about eigenvectors, matrix decompositions, and vector spaces. I dutifully memorized formulas for exams, promptly forgot them, and thought: “When will I ever use this?”
Fast forward to 2025. I’m building a RAG system, knee-deep in semantic search, and suddenly I’m computing dot products like my career depends on it. Because, well, it kind of does.
The punchline? That “useless” linear algebra is now the foundation of how we search through billions of documents, understand meaning, and make AI systems actually useful.
So here’s my redemption arc: Linear algebra isn’t boring. We just taught it wrong.
Why Everyone Suddenly Cares About Vectors
If you’ve built anything with RAG (Retrieval-Augmented Generation) in the last two years, you’ve heard the pitch: “Turn your text into embeddings, then search semantically!”
But what ARE embeddings, really?
Think of embeddings as coordinates in meaning-space. Every word, sentence, or document gets converted into a list of numbers (a vector) that captures its semantic essence.
“cat” = [0.2, 0.8, 0.1, 0.4, ...] (hundreds or thousands of numbers)
“kitten” = [0.19, 0.79, 0.09, 0.41, ...] (very close!)
“car” = [0.7, 0.1, 0.9, 0.2, ...] (far away)Words with similar meanings get similar coordinates. It’s like GPS, but for concepts.
Why this matters for RAG
Traditional keyword search is dumb. If you search for “automobile” and the document says “car,” you get nothing. Zero. Nada.
Embeddings are smart. They know “automobile” and “car” point to nearly the same spot in meaning-space. So when you search, you find what you meant, not just what you said.
This is why every RAG system worth its salt uses embeddings. They’re the difference between “404 Not Found” and “Here’s exactly what you need.”
· · ·
The Tale of Two Similarity Metrics
So you’ve got your vectors. Now you need to answer: “How similar are these two things?”
Enter our protagonists: Cosine Similarity and Dot Product.
If you remember high school math class (barely), the dot product is that thing where you multiply corresponding elements and add them up. Simple enough.
But cosine similarity? That’s where things get interesting.
Cosine Similarity
Cosine similarity measures the angle between two vectors. If they point in the same direction, similarity = 1. Opposite directions = -1. Perpendicular = 0.
cosine_similarity(A, B) = (A · B) / (||A|| × ||B||)It’s called “cosine” because this formula literally computes the cosine of the angle between vectors. (That’s right, we’re doing trigonometry in high-dimensional space. College you would be proud.)
Why it’s famous: It ignores magnitude and focuses purely on direction. A short document and a long document about the same topic get the same similarity score. Fair and intuitive.
Dot Product
The dot product is simpler: just multiply corresponding elements and sum them up.
dot_product(A, B) = (a₁ × b₁) + (a₂ × b₂) + ... + (aₙ × bₙ)No division. No square roots. Just multiply and add.
The catch: It cares about magnitude. Bigger vectors naturally get bigger scores, which can be... problematic.
· · ·
The Key Difference (And Why It Matters)
Here’s where it gets interesting.
Cosine similarity normalizes by magnitude. Think of it like this:
A · B = ||A|| × ||B|| × cos(θ)The dot product equals: (magnitude of A) × (magnitude of B) × (cosine of angle).
Cosine similarity divides this by both magnitudes, isolating just the angle:
cos(θ) = (A · B) / (||A|| × ||B||)In plain English
Dot product = “How much do these vectors point in the same direction, weighted by their size?”
Cosine similarity = “Do these vectors point in the same direction, period?”
Why This Distinction Matters
Imagine two documents:
Doc 1: “Cats are cute”
embedding magnitude = 0.8
Doc 2: “Cats are cute and fluffy and wonderful and purr and have whiskers...”
embedding magnitude = 2.3Both are about cute cats. They should be considered similar.
Using dot product (unnormalized): Doc 2 gets a way higher score just because it’s longer. Unfair!
Using cosine similarity: Both get the same score (≈ 1.0) because they point in the same direction. Fair!
This is why cosine similarity became the gold standard for semantic search. It’s magnitude-invariant – it doesn’t penalize short documents or favor long ones.
Which Approach Should You Use?
Before we dive deeper, here’s the cheat sheet:
Full-text with rewriting costs about $0.001 per query with latency under 50ms. This works for about 60% of systems and offers zero re-indexing.
Hybrid on-the-fly approaches cost around $0.015 per query with 200-500ms latency. These work well for high churn data and provide perfect freshness.
Hot/cold tiers balance cost at $0.005 per query with 50-100ms latency. This offers the best trade-off for balanced needs.
Full pre-embedding is the cheapest at $0.0005 per query with latency under 50ms. This approach works for massive scale and provides maximum speed.
Keep this in mind as we explore the theory behind why these trade-offs exist.
The Plot Twist: Cosine Similarity Might Be Overkill
Here’s where our story takes a turn.
Most modern embedding APIs – OpenAI’s text-embedding-3, Cohere, Voyage AI – return normalized embeddings by default. (Always check your model’s documentation, but chances are, they’re normalized.)
What does “normalized” mean? Every vector has a magnitude of exactly 1.
And when both vectors have magnitude = 1:
cosine_similarity(A, B) = (A · B) / (1 × 1) = A · B
The dot product and cosine similarity are IDENTICAL.
So all that careful normalization we’ve been doing? That division by magnitudes? We’re dividing by 1. It’s like wearing a belt with suspenders.
Why This Changes Everything
Speed: Dot product is faster. No square roots, no division. Just multiply and add.
In high-scale systems (millions of documents, thousands of queries per second), this matters. Vector databases optimize for dot product similarity when working with normalized vectors for exactly this reason.
Simplicity: Less code, fewer bugs. One operation instead of three.
Performance: Every millisecond counts when you’re paying for compute at scale.
So Is Cosine Similarity Dead?
Not quite.
Use cosine similarity when: Your embeddings are NOT pre-normalized. You’re dealing with unnormalized vectors (TF-IDF, custom embeddings). Document length shouldn’t affect similarity. You need interpretable scores (0-1 range).
Use dot product when: Your embeddings ARE pre-normalized (most modern models). You need maximum speed. You’re using a vector database with normalized vectors. You’re processing millions of comparisons.
The trick: Check if your model normalizes embeddings. If yes, use dot product. If no, normalize once at index time, then use dot product forever.
Cosine similarity isn’t dead. It’s just... optimized away.

