Best Practices
Rules for writing correct, idiomatic DataChain code. Follow these to avoid the most common pitfalls.
Import Convention
Always Type Your Python Operations
Every function passed to map(), gen(), or agg() must have a return type annotation. Missing annotations default to str and crash at runtime.
Prefer Type Hints Over params/output
Rely on function annotations for auto-inference. Use params= only for nested column binding. Use output= only for non-str lambdas.
save() Before filter() on Expensive Operations
Filtering discards work that was never persisted. Save expensive Python operation output first, then filter.
Materialize Before Reuse
When the same chain is consumed by multiple terminal operations, save() or persist() it first.
Use settings(parallel=True) for Expensive Operations
Always parallelize ML inference, LLM calls, and heavy I/O. Omit for lightweight operations.
Multiple Signals Per map()
map() accepts multiple keyword-function pairs; all functions run in a single pass over the rows, so it's faster than chaining separate .map() calls. If one function's parameter name matches another's output name, it receives that function's result (kwarg order doesn't matter).
For related fields produced together (e.g. by one LLM call), return a Pydantic model instead:
Use Native Analytics, Not Pandas
Use group_by, count, sum, avg instead of materializing to pandas.
Trailing Slash in Storage Paths
Always append / to bucket and prefix URIs.
anon=True for Public Buckets
Pass anon=True explicitly or the call stalls or returns 403.
Glob Patterns Inside read_storage()
Put glob patterns inside read_storage() so they appear in lineage.
Prefer setup() Over Stateful Classes
Use .setup(x=lambda: init()) instead of class-based Mapper when you don't need teardown.
Avoid File Download for Metadata-Only Operations
Use params=["file.path"] to avoid downloading files when you only need path metadata.
Merge, Don't Build Dicts
Read all sources as chains and merge. Never build Python dicts outside the chain.
Always Use DataChain for File Access
Never use os.walk, glob.glob, or pathlib for accessing data files. Always use DataChain APIs.
select_except() After Merge
Drop duplicated columns after merge. Do all merges first, one select_except() at end.
Single File vs Multi-File
Use dc.File.at() for one file, read_storage() for directories.
Shared Listing Prefix
Use a common parent prefix for multiple read_storage() calls; you get one listing and cache hits.
Inline func Expressions
Pass dc.func.* directly to on=, partition_by=. Don't mutate() throwaway columns.