Coding Fundamentals
The Python and SQL patterns FDE-DE technical screens actually reach for. Less algorithmic than a SWE loop, more practical — write something you'd actually ship to a customer next week.
What to expect
FDE-DE coding rounds are typically 45–60 minutes, one or two problems. The problems are framed in domain language (contracts, extractions, suppliers) rather than abstract algorithms. The interviewer is looking for:
- Clean Python that someone at the customer would accept — types, small functions, clear names.
- Fast SQL over messy data — handles NULLs, fanouts, dedupe deliberately.
- Narrating tradeoffs as you write — "I could use a dict here, or a set, here's why I'm picking dict."
- Domain-credible reasoning — when given a contract / supplier / spend prompt, you understand what the data actually represents.
- Sensible follow-up handling — when the interviewer adds a wrinkle, you adapt rather than rewrite.
Python patterns
Dict-based aggregation
from collections import defaultdict, Counter
# Sum spend by supplier
spend_by_supplier: dict[str, float] = defaultdict(float)
for row in invoices:
spend_by_supplier[row["supplier_id"]] += row["amount"]
# Count documents per class
counts = Counter(d["doc_class"] for d in documents)
# Group rows for further processing
by_supplier: dict[str, list[dict]] = defaultdict(list)
for inv in invoices:
by_supplier[inv["supplier_id"]].append(inv)
Pure functions with types
from dataclasses import dataclass
@dataclass(frozen=True)
class Extraction:
document_id: str
field: str
value: str
confidence: float
def above_threshold(
extractions: list[Extraction],
threshold: float = 0.85,
) -> list[Extraction]:
"""Keep extractions with confidence at or above threshold."""
return [e for e in extractions if e.confidence >= threshold]
Type hints, frozen dataclass, narrow function. This shape is what FDE-DE reviewers want to see — your interviewer can imagine pasting this into a deployment.
Generators for streams
When the document corpus doesn't fit in memory, use generators.
from collections.abc import Iterator
import json
def stream_extractions(path: str) -> Iterator[dict]:
"""Yield one extraction at a time from a JSON Lines file."""
with open(path) as f:
for line in f:
yield json.loads(line)
# Compose with itertools / functions, never load everything at once
total = sum(e["confidence"] for e in stream_extractions("extractions.jsonl"))
Cleaning & reshaping
The "messy input" pattern
The most common FDE-DE coding problem shape: "here's a CSV / JSON / Excel of contract data, write a script that cleans and reshapes it for downstream use."
import csv
from datetime import datetime
from typing import Iterable
def clean_date(s: str | None) -> str | None:
"""Normalize various date formats to ISO. Return None if unparseable."""
if not s or not s.strip():
return None
s = s.strip()
for fmt in ("%Y-%m-%d", "%m/%d/%Y", "%d-%b-%Y", "%B %d, %Y"):
try:
return datetime.strptime(s, fmt).date().isoformat()
except ValueError:
continue
return None # unparseable; downstream should flag
def normalize_supplier(name: str | None) -> str | None:
"""Strip whitespace, collapse internal whitespace, title-case."""
if not name: return None
cleaned = " ".join(name.split()).strip()
# Strip common corp suffixes for canonical name
for suffix in (", Inc.", " Inc.", ", LLC", " LLC", ", Ltd", " Ltd"):
if cleaned.endswith(suffix):
cleaned = cleaned[:-len(suffix)]
break
return cleaned or None
def clean_contract_row(row: dict) -> dict:
return {
"contract_id": row["id"].strip(),
"supplier": normalize_supplier(row.get("supplier_name")),
"effective_date": clean_date(row.get("effective_date")),
"value_usd": float(row["value"]) if row.get("value") else None,
}
The senior touches:
- Returning None on unparseable input rather than crashing — let the caller decide.
- Small focused functions per field type.
- Multiple date formats handled — real customer data has all of them.
- Supplier suffix stripping — domain-specific normalization.
Fuzzy matching
Almost every FDE-DE round has a fuzzy-join wrinkle. Memorize a working implementation.
from difflib import SequenceMatcher
def similarity(a: str, b: str) -> float:
"""Normalized similarity between 0 and 1."""
return SequenceMatcher(None, a.lower(), b.lower()).ratio()
def best_match(query: str, candidates: list[str], threshold: float = 0.8) -> str | None:
"""Return the best candidate match if above threshold, else None."""
scored = [(c, similarity(query, c)) for c in candidates]
scored.sort(key=lambda x: x[1], reverse=True)
if not scored or scored[0][1] < threshold:
return None
return scored[0][0]
What to mention unprompted:
- "For 10k+ candidates, I'd preprocess into n-gram or trigram buckets first to avoid O(N) per query."
- "For names specifically, RapidFuzz is the production-grade library — much faster than SequenceMatcher."
- "For truly large-scale name matching, I'd use embedding similarity + a vector index."
SQL patterns
Refer to 07-sql-and-data-modeling for the contract-data-specific patterns. The reusable patterns:
- Window functions for "latest per group" —
ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ... DESC)then filterWHERE rn=1. - Anti-joins for "missing" —
LEFT JOIN ... WHERE right.key IS NULLfor orphans, orNOT EXISTSfor cleaner semantics. - CTEs as outline — one CTE per logical step, named descriptively.
- JSON / JSONB navigation — Postgres
->>, Snowflake:, BigQueryJSON_VALUE. The customer's warehouse dictates the syntax. - QUALIFY clause — Snowflake / BigQuery support
QUALIFY ROW_NUMBER() OVER (...) = 1directly, avoiding the subquery.
The senior SQL signal
Use CTEs aggressively. Name them like prose. Comment when the logic isn't obvious. The interviewer reads the CTE names as the outline of your thinking — bad names ("t1", "step2") read junior.
Live-coding tips for FDE-DE
- Clarify before coding. Restate the problem. Ask about input format, expected output, edge cases. FDE rounds reward this disproportionately.
- State approach + tradeoffs out loud. "I'm going to use a dict because we'll have N keys and look them up M times. Could also sort and binary-search; not worth it for our N."
- Write small functions with types. Even at 15-minute scale, types and small functions read better than monolithic blocks.
- Handle NULLs deliberately. Customer data has nulls everywhere. Show you've thought about which fields can be null and what to do when they are.
- Trace one example through. After you write it, walk a sample row through the code out loud. Catches off-by-ones immediately.
- Anticipate the production follow-up. "What if the input is 100M rows?" "What if the customer wants this hourly instead of daily?" Have answers.
- For SQL: name your CTEs descriptively. The interviewer reads CTE names as outline.
- Cite customer context when asked about tradeoffs. "If this is a one-off audit for the customer, I'd just do X. If it'll run weekly in production, I'd refactor to Y." The contextual answer signals FDE thinking.
✓ Check yourself
Answer from memory before opening each one.
-
The chapter's date cleaner tries month-first before other formats. What does it return for "03/04/2024", and when is that wrong?
Show answer
It returns 2024-03-04, March 4. For a European source it should be April 3, and no code can tell from the string alone. Settle the format per source with the customer, and flag ambiguous values instead of guessing. See cleaning and reshaping.
-
Predict the supplier normalizer's output for "Acme Corp, Inc." and for "ACME CORP, INC.". What does the second result mean for a later join?
Show answer
The first becomes "Acme Corp". The second is unchanged, because the suffix check is case-sensitive. The two names then fail to match each other, so lowercase for comparison or normalize case first. See cleaning and reshaping.
-
You sum confidence over a 40 GB lines file with a generator, then want the maximum too. What goes wrong if you reuse the same generator?
Show answer
A generator is single-pass, so the second use sees nothing. Either compute both in one loop or call the function again to reopen the file. Loading everything into a list would avoid it but defeat the purpose. See Python patterns.
-
The nearest-candidate function scans every candidate for each query. With 50,000 queries and 200,000 candidates, what is the problem and what are the options?
Show answer
That is ten billion comparisons. Bucket candidates by trigram or prefix first, use a faster library such as RapidFuzz, or move to embeddings with a vector index. State the scaling issue before the interviewer asks. See fuzzy matching.
-
Write the shape of the query that returns the latest extraction per document in Postgres and in Snowflake, and say why the customer's warehouse matters.
Show answer
Rank rows with a window function partitioned by document and ordered by extraction time descending, and keep rank one. Snowflake can filter directly with QUALIFY, while Postgres needs a subquery or CTE. The customer's engine decides which syntax they can run and maintain. See SQL patterns.
-
The same cleaning task is a one-off audit in one case and a weekly production job in another. What changes in your code?
Show answer
For the audit, a clear script that logs oddities is enough. For production, add tests, stable output, logging of unparseable values to a side channel, idempotent output and handling for volume. The context decides how much craft to spend. See live-coding tips for FDE-DE.
Takeaway
- Coding rounds run 45 to 60 minutes in domain language, and are graded on clean typed code, SQL that handles NULLs and fanout, narrated trade-offs, and adapting to wrinkles.
- Return None on unparseable input and let the caller flag it; keep functions small and typed, one per field kind.
- Date formats are ambiguous without source knowledge, and the order you try them decides the answer.
- Use generators when the corpus will not fit in memory, and remember they can be read once.
- The standard-library matcher is a baseline that costs one pass per query; at scale, bucket candidates, use RapidFuzz or use vectors.
- For SQL, use window functions for latest-per-group, NOT EXISTS for missing rows, named CTEs, and QUALIFY where the engine has it.
- Clarify first, trace one example, and expect the follow-up about 100 million rows.
Next is a set of practice problems in the same shapes. → Coding Problems
Further reading
- difflib: Helpers for computing deltas — Python documentation, accessed 2026. SequenceMatcher and its limits as a similarity measure.
- dataclasses: Data Classes — Python documentation, accessed 2026. Frozen dataclasses for immutable records.
- datetime: Basic date and time types — Python documentation, accessed 2026. How strptime parses formats, which is why order matters.
- RapidFuzz — RapidFuzz project documentation, accessed 2026. A fast string matching library for production use.
- QUALIFY — Snowflake documentation, accessed 2026. Filtering on window function results without a subquery.