Course Content
Live Coding Interview Prep
7 sections · 50 lessons
Write code for fixed-size and recursive text chunking.
What you need to know
A chunk is the unit you embed and retrieve. Its size is a trade-off:
- Too big — one vector has to represent several topics, so it matches none of them well, and each retrieved chunk fills the prompt with text that does not answer the question.
- Too small — a chunk lacks the context to be understood ("It takes 5 days." — what does?).
Typical sizes are a few hundred tokens (roughly 1,000–2,000 characters) with a 10–15% overlap.
Fixed-size
- Cut every
sizecharacters, stepsize − overlap - O(n), no knowledge of the text
- Splits words and sentences anywhere
- Fine for logs or code with no structure
Recursive
- Split on
\n\n, then\n, then., then - Recurse only into pieces still too large
- Keeps paragraphs and sentences whole
- LangChain's
RecursiveCharacterTextSplitterdefault
Recursion here means: a piece that is still too long after splitting on paragraphs is split again with the next separator in the list. Each level gets a shorter separator list, and the final fallback is an unconditional hard cut, so the recursion always ends.
1def fixed_chunks(text: str, size: int = 500, overlap: int = 50) -> list[str]:2 """Windows of `size` chars; each starts `size - overlap` after the previous one."""3 if size <= 0 or not 0 <= overlap < size:4 raise ValueError("need size > 0 and 0 <= overlap < size")5 chunks, start = [], 06 while start < len(text):7 piece = text[start:start + size]8 if piece.strip():9 chunks.append(piece)10 if start + size >= len(text): # reached the end: no tail fragment11 break12 start += size - overlap13 return chunks1415SEPARATORS = ("\n\n", "\n", ". ", " ")1617def recursive_split(text: str, size: int = 500, separators=SEPARATORS) -> list[str]:18 """Chunks of at most `size` chars, cut at the coarsest separator that works."""19 text = text.strip()20 if len(text) <= size:21 return [text] if text else []22 for i, sep in enumerate(separators):23 if sep not in text:24 continue25 pieces = text.split(sep)26 parts = [p + sep for p in pieces[:-1]] + [pieces[-1]] # keep the separator27 chunks, current = [], ""28 for part in parts: # greedy packing29 if current and len(current + part) > size:30 chunks.append(current)31 current = ""32 current += part33 chunks.append(current)34 out = []35 for c in chunks:36 if len(c.strip()) <= size:37 out += [c.strip()] if c.strip() else []38 else: # still too big: finer separator39 out.extend(recursive_split(c, size, separators[i + 1:]))40 return out41 return [text[j:j + size] for j in range(0, len(text), size)] # no separator left: hard cutThe tricky parts:
- The stop test in
fixed_chunks. Afor i in range(0, len(text), step)loop keeps going after a window has already reached the end, producing a tail fragment fully contained in the previous chunk. Stopping oncestart + size >= len(text)fixes it. - Keeping the separator (
p + sep). Splitting"A. B"on". "and joining with nothing loses the full stop; attaching the separator to the end of each piece keeps the text intact. current and …— a piece is pushed only if the buffer is non-empty, so a single oversized part never produces an empty chunk before it; it goes to the recursive branch instead.separators[i + 1:]— every level has strictly fewer separators, which is the termination argument.
Complexity: fixed_chunks is O(n) time and O(n) space for the output (plus the overlap copies). recursive_split splits and scans the text once per separator level, so O(n·s) time for s separators, and O(n) space.
A real-life example
A short refund-policy page with size=40:
1page = ("Refund policy\n\nRefunds take 5 days. They go to the original UPI id.\n"2 "COD orders get store credit.\n\nContact us 24x7.")3for c in recursive_split(page, 40):4 print(len(c), repr(c))5# 13 'Refund policy'6# 20 'Refunds take 5 days.'7# 31 'They go to the original UPI id.'8# 28 'COD orders get store credit.'9# 16 'Contact us 24x7.'10print(fixed_chunks("abcdefghij", size=4, overlap=1)) # ['abcd', 'defg', 'ghij']| level | separator | piece too big? | action |
|---|---|---|---|
| 1 | \n\n | the middle paragraph (81 chars) | recurse with \n |
| 2 | \n | "Refunds take 5 days. They go to…" (52 chars) | recurse with . |
| 3 | . | both sentences fit alone, not together | two chunks |
Every chunk is a whole sentence or heading. A fixed 40-character cut of the same page would start its second chunk in the middle of "Refunds".
Policy documents, product manuals and HR handbooks behind company chatbots are chunked this way before embedding.
Follow-up questions to expect
- "Characters or tokens?" — Embedding models and context windows count tokens, so production splitters measure length with the tokenizer (pass a
length_function). Characters are a fine approximation in an interview. - "How would you add overlap to the recursive splitter?" — Prepend the last sentence (or last k characters) of the previous chunk to each chunk, and account for it in the size limit so chunks do not exceed
size. - "What about Markdown or HTML?" — Split on the document's own structure first (headings, sections), and prepend the heading path ("Refunds > UPI") to each chunk; it is free context that helps retrieval.