Live Coding Interview Prep

Course Content

Live Coding Interview Prep

7 sections · 50 lessons

Write code for fixed-size and recursive text chunking.


How the refund page is split at size 40Blank lines: 3 pieces, one is 81 charsNewline: its 52-char first line too longSentence end: two chunks of 20 and 31Space or hard cut: not needed here
A finer separator is tried only on the piece that is still too big, so every chunk stays a whole sentence.

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 size characters, step size − 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 RecursiveCharacterTextSplitter default

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.

Python
def fixed_chunks(text: str, size: int = 500, overlap: int = 50) -> list[str]:    """Windows of `size` chars; each starts `size - overlap` after the previous one."""    if size <= 0 or not 0 <= overlap < size:        raise ValueError("need size > 0 and 0 <= overlap < size")    chunks, start = [], 0    while start < len(text):        piece = text[start:start + size]        if piece.strip():            chunks.append(piece)        if start + size >= len(text):          # reached the end: no tail fragment            break        start += size - overlap    return chunksSEPARATORS = ("\n\n", "\n", ". ", " ")def recursive_split(text: str, size: int = 500, separators=SEPARATORS) -> list[str]:    """Chunks of at most `size` chars, cut at the coarsest separator that works."""    text = text.strip()    if len(text) <= size:        return [text] if text else []    for i, sep in enumerate(separators):        if sep not in text:            continue        pieces = text.split(sep)        parts = [p + sep for p in pieces[:-1]] + [pieces[-1]]     # keep the separator        chunks, current = [], ""        for part in parts:                                        # greedy packing            if current and len(current + part) > size:                chunks.append(current)                current = ""            current += part        chunks.append(current)        out = []        for c in chunks:            if len(c.strip()) <= size:                out += [c.strip()] if c.strip() else []            else:                                                 # still too big: finer separator                out.extend(recursive_split(c, size, separators[i + 1:]))        return out    return [text[j:j + size] for j in range(0, len(text), size)]   # no separator left: hard cut

The tricky parts:

  • The stop test in fixed_chunks. A for 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 once start + 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:

Python
page = ("Refund policy\n\nRefunds take 5 days. They go to the original UPI id.\n"        "COD orders get store credit.\n\nContact us 24x7.")for c in recursive_split(page, 40):    print(len(c), repr(c))# 13 'Refund policy'# 20 'Refunds take 5 days.'# 31 'They go to the original UPI id.'# 28 'COD orders get store credit.'# 16 'Contact us 24x7.'print(fixed_chunks("abcdefghij", size=4, overlap=1))     # ['abcd', 'defg', 'ghij']
levelseparatorpiece too big?action
1\n\nthe 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 togethertwo 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.