On this page
- Chunking sets the ceiling on everything downstream
- The five splitting methods and what each one destroys
- The Chunk Contract: four clauses every chunk should satisfy
- Overlap is insurance, not a strategy
- Index small, send big
- Tables, code and the documents that break every splitter
- Sizing your corpus, with the arithmetic exposed
- How to test a chunking change without fooling yourself
- A default worth starting from
Chunking sets the ceiling on everything downstream
A chunk is the smallest unit your system can retrieve and the smallest unit it can cite, so the split decides which facts are reachable at all. Cut a claim away from the heading that scoped it and the fact is still in your corpus and permanently unreachable, no matter how good your embedding model, your reranker or your prompt is. Chunking is the only stage in a RAG pipeline whose mistakes cannot be repaired further down.
- One claim per chunkA chunk holding two unrelated claims retrieves for both and answers neither cleanly. A chunk holding half a claim answers nothing.
- Structure beats sizeSplitting on document structure preserves what a fixed character count destroys. Token count is a cap, not a strategy.
- Index small, send bigRetrieve precise units and expand to the surrounding section at assembly time. Precision and context are not the same trade-off.
- Every change is a re-indexAltering the splitter means re-embedding everything and recalibrating every score threshold you tuned. Budget the change, do not experiment casually in production.
- Chunk
- A passage of source text stored and indexed as a single retrievable unit, carrying its own identifier and metadata so it can be found, ranked and cited on its own.
- Overlap
- A number of tokens repeated at the boundary between neighbouring chunks, so a claim sitting across a cut still appears whole in at least one of them.
- Parent-child retrieval
- A pattern where small chunks are indexed and searched for precision, but the larger section containing the winning chunk is what gets sent to the model.
- Contextual header
- A short generated preamble attached to a chunk before embedding, naming the document, the heading trail and the situation the passage refers to, so the chunk reads correctly in isolation.
- Late chunking
- Embedding a long passage first so every token sees the full context, then pooling the token vectors into chunk vectors afterwards, rather than embedding each chunk in isolation.
The five splitting methods and what each one destroys
There are five methods in real use and they form a ladder of cost against fidelity. Most teams should start at structure-aware, not at fixed-size, because the extra day of work removes the largest category of unreachable facts.
| Method | How it cuts | What it destroys | Use it when |
|---|---|---|---|
| Fixed size | Every N tokens, ignoring the text entirely | Sentences, claims, table rows, the heading that scoped the passage | The corpus is unstructured plain text and you need a baseline today |
| Recursive character | Tries paragraph breaks, then sentence breaks, then characters, until the piece fits | Less than fixed size, but still cuts blindly once a section exceeds the cap | You need a sane default across mixed formats with no parsing effort |
| Structure aware | On real document structure: headings, list items, table boundaries, code blocks | Very little, provided the parser preserved the structure in the first place | The corpus has structure, which covers most documentation, policies and contracts |
| Semantic | Where the embedding similarity between consecutive sentences drops below a threshold | Nothing obvious, but boundaries move whenever you change the embedding model or the threshold | Prose with no headings, such as transcripts, interviews and long-form reports |
| Late chunking | Cuts after embedding, so each chunk vector was computed while the whole document was visible | Nothing, but it needs an embedding model with a long input and more compute per document | Chunks lose meaning without their surroundings and you can afford the indexing pass |
A target of roughly 300 to 500 tokens with a hard ceiling is a reasonable band for documentation and policy text, because it usually holds one complete idea and still leaves room to send several passages. Treat it as a starting point to measure, not a rule. What matters is that the boundary falls on a real edge in the document.
The Chunk Contract: four clauses every chunk should satisfy
Stop asking how big a chunk should be and start asking what a chunk owes the rest of the system. Four obligations cover it, each with a test you can run on a random sample in an hour.
The Chunk Contract
Sample fifty chunks at random from your index and check each clause by hand. The clause you fail most often tells you exactly which part of the pipeline to fix, and the exercise takes about an hour.
A colleague reading only this chunk can tell what it is about, who it applies to and when it took effect. Test: cover the document name and read it aloud. If it opens with 'this' or never names the product or the period, it fails. The fix is a contextual header, not a bigger chunk.
One dominant answerable proposition per chunk. Two unrelated claims means it retrieves for both queries and dilutes its own embedding, since a vector is an average and averaging two topics lands you between them, near neither. Test: list the questions this chunk answers. More than about three and it should be split.
The chunk carries a stable id, a source path, a version and a character range, so an answer can cite the exact place. Test: take a chunk id from a log entry three weeks old and try to reach the source. If the id was a row number in a table you rebuild nightly, it fails.
The chunk knows its neighbours and its parent section, so retrieval can expand context after ranking. Test: given a chunk id, can you fetch its section in one lookup? If not, every chunk has to be both precise and complete, and nothing is both.
Overlap is insurance, not a strategy
Overlap repeats a slice of text at each boundary so a claim cut in half still appears whole somewhere. It is cheap insurance against the boundary problem and it is not a fix for bad boundaries, because it duplicates content into the index and duplicated content wins twice in the same result list.
If you use overlap and do not dedupe, your top-k is a lie. Two chunks sharing most of their text both score highly on the same query, so you pay for five passages and send the model two. Compare candidates before assembly, drop any whose text is largely contained in a higher ranked one, and refill from further down. That is twenty lines of code and the highest return change most retrieval stacks are missing.
Index small, send big
Precision and context pull in opposite directions, and you do not have to choose. Index a small unit so the vector is sharp and the match is specific, then expand to the parent section at assembly time so the model has enough surrounding material to reason with. The pattern goes by several names and it is the single biggest quality jump available after hybrid retrieval.
Three ways to expand
- Sentence window. Index individual sentences, send the sentence plus a few either side. Best for dense reference material where the answer is one line and the neighbours qualify it.
- Parent document. Index paragraph-sized chunks, send the whole section containing the winner. Best for policy and documentation, where the heading and the exceptions matter.
- Summary index. Index a generated one-line summary of each section, send the full section. Best for long narrative documents where the literal wording matches nobody's question.
Expansion has a cost people forget: your context grows by the expansion factor, on every question, forever. Index at 150 tokens and send parents of 800 and your input tokens went up more than five times, which appears on the bill rather than in a metric. Cap the expansion and check the arithmetic in RAG explained for builders.
The contextual header, which is cheaper than it looks
Before embedding, prepend a block naming the document, the heading trail, the version and one generated sentence situating the passage. It costs one small call per chunk at ingest, once, and it fixes clause one for the whole corpus. It also improves keyword search, because the product name and version now sit in the chunk text where BM25 can see them.
# 1. The stored chunk format. Everything above the rule is generated
# at ingest and prepended before embedding, so the chunk reads alone.
{doc_title} > {heading_path}
Version {version}, effective {effective_date}. Applies to: {audience}
Context: {situating_line}
---
{chunk_text}
# 2. The prompt that writes situating_line, one call per chunk at ingest.
# Keep it short. This is a situating sentence, not a summary.
SYSTEM:
You are given a whole document and one passage from it. Write ONE sentence,
under 30 words, that lets a reader understand the passage without the
document. Resolve pronouns and vague references to their actual subject.
Name the entity, the product, the version and the time period the passage
is about, but only if the document states them. Do not add facts. Do not
summarise the passage. Do not use the words "this passage" or "the document".
If the passage is already self-explanatory, output the single word: NONE.
USER:
<document>{full_document}</document>
<passage>{chunk_text}</passage>
# Worked example
# passage: "The window is 14 days from the invoice date."
# situating_line: "Refund window for annual Enterprise plans bought after
# the January 2026 pricing change."
Tables, code and the documents that break every splitter
Some content cannot be cut by any general rule and needs a specific one. Tables are the worst offender, because a table split across chunks produces rows that have lost their column headers, and a row of numbers with no headers is worse than nothing. It is confidently retrievable and meaningless.
| Content | What goes wrong by default | The rule that fixes it |
|---|---|---|
| Tables | Rows split away from headers, or the whole table flattened into a run-on line | Keep small tables whole in one chunk. For large ones, serialise each row with its column names repeated, plus a caption chunk describing the table |
| Code blocks | Split mid-function, so the retrieved fragment does not compile or explain anything | Never split inside a fenced block. If the block exceeds the cap, keep it whole and accept an oversized chunk |
| Scanned PDFs | The extractor returns nothing or returns page furniture only, and it exits cleanly | Detect empty or near-empty extractions at ingest, route to OCR, and fail the ingest loudly if the page yields no text |
| Multi-column layouts | Text is read across columns, interleaving two unrelated sentences into one | Use a layout-aware extractor and spot-check the output visually. This one is invisible in the logs and obvious to the eye |
| Spreadsheets | Treated as text, producing thousands of near-identical low-value chunks | Do not embed them. Query them. Route numeric and aggregate questions to the data, not the retriever |
Print a random sample of your chunk store and read it. Teams that skip this discover months later that a large slice of the corpus was navigation boilerplate, cookie banners or a repeated legal footer, all of which embed into confident near-duplicate matches for any question. Ingestion problems masquerade as retrieval problems. Extracting data from documents covers the parsing side, and our document processing work exists mostly because this stage is where corpora go wrong.
Sizing your corpus, with the arithmetic exposed
Before choosing a chunk size, run the numbers on your own corpus. The two figures that matter are how many vectors you will store, which drives index cost and query latency, and how many tokens each question will carry, which drives the recurring bill.
The embedding price is a stand-in so the arithmetic runs, and it is not a quote. Substitute your provider's current figure. Note that halving the chunk size roughly doubles the vector count while leaving the tokens you send per question unchanged, until you start expanding to parents.
How to test a chunking change without fooling yourself
Change one thing, hold everything else frozen, and look at the questions that got worse rather than at the average. Chunking changes almost always improve the mean and almost always break a specific class of question, and the mean will happily hide that from you until a user finds it.
- Freeze the retriever, the reranker and the model
Same embedding model, same fusion weights, same top-k, same prompt, same model version. Two changes at once teaches you nothing and buys a week of arguing about which one did it.
- Use a question set you did not generate
Real user questions with hand-labelled correct sources. Model-generated questions about documents the model just read score well under any scheme, because they are phrased in the source's own words, which is exactly the case chunking does not need to handle.
- Score recall@5 and recall@20 per question, not just the mean
Store the per-question result for both runs side by side. The mean tells you the direction. The per-question table tells you the truth.
- Read every question that went from found to missed
This list is the actual result of the experiment. It usually clusters: every table lookup broke, or every question about exceptions broke because the exceptions now sit in their own chunk with no heading.
- Check the citation surface, not only the hit rate
A smaller chunk can retrieve correctly and still produce a worse answer, because the model receives a fragment without the qualifying sentence. Read ten full answers, not just the retrieval scores.
- Price the migration before you commit
A chunking change means a full re-embed, a re-index and recalibrating every threshold including your abstention floor. Keep the old index until the new one is scored, and never swap the splitter and the embedding model in one release.
A default worth starting from
Here is a configuration that is hard to beat as a starting point for documentation, policies, contracts and knowledge base content. It is not a law and every clause of it should be measured on your own gold set. It exists so you can stop reading and start building today, then improve from a known position instead of from a blank page.
Once the split is right, the next failure to hunt is the one where retrieval works, the correct passage is in context, and the answer is still wrong. That is a different mechanism, covered in why your RAG system gives confident wrong answers.
Questions readers ask next
What is the best chunk size for RAG?
How much overlap should I use between chunks?
Is semantic chunking better than fixed size chunking?
Should I re-chunk my whole corpus if I change the strategy?
How do I stop tables being destroyed during chunking?
What is parent-child retrieval and when should I use it?
ChatGPTalker. "Chunking Strategies That Change Your Answers." chatgptalker.com, 2026-08-26. https://chatgptalker.com/guides/chunking-strategies/