Skip to content

Core 10: Team Adoption – Coding Standards, Patterns, and Review Guidelines for FP-Style Python

Module 09

Core question:
How do you establish coding standards, recognize key patterns, and implement review guidelines to facilitate team adoption of FP-style Python, ensuring consistent, maintainable, and high-quality functional code in projects like FuncPipe?

In this final core of Module 09, we focus on strategies for team adoption of functional programming in Python within the FuncPipe RAG Builder (now at funcpipe-rag-09). Adopting FP requires shared standards, recognizable patterns, and structured reviews to build team alignment, reduce onboarding friction, and maintain code quality. We provide templates for coding standards (e.g., purity defaults, immutability), common FP patterns (e.g., pipe composition, monadic error handling), and review checklists (e.g., checking for side effects, test coverage). These build on prior cores, integrating with tools like mypy, Hypothesis, and linters (e.g., ruff with rules like B006 for mutable defaults). We "refactor" the RAG team workflow by simulating adoption: start with mixed imperative/FP code; end with standardized FP practices. Sharp edges: Resistance to change (address via gradual pilots); over-rigidity (balance with pragmatic escapes). No new deps; use stdlib + prior tools (ruff, mypy, Hypothesis, pre-commit); toolz optional from Core 2.

Motivation Bug: Without standards and reviews, FP adoption leads to inconsistent code (e.g., mixed mutation/purity), review disputes, and maintenance issues; guidelines foster consensus and quality.

Delta from Core 9 (Module 09): Specs enable cross-process decoupling; this core enables cross-team consistency via standards and reviews.

Adoption Protocol (Contract, Entry/Exit Criteria): - Standards: Documented rules (e.g., PEP 8 + FP extensions) for purity, types, patterns. - Patterns: Reusable idioms (e.g., Reader for config) with examples. - Reviews: Checklists enforcing laws (e.g., no globals); pull requests require FP compliance. - Semantics: Laws like consistency (same patterns yield similar code); gradualism (start small, measure wins); verified via mock reviews and team surveys. - Integration: Apply to RAG; extend Module 7 migration with team processes. - Mypy Config: --strict; add FP linters like ruff. Audience: Team leads promoting FP in Python teams, focusing on scalability and collaboration. Outcome: 1. Draft FP coding standards for your team. 2. Identify and apply key FP patterns in code. 3. Implement review guidelines and checklists.


1. Enforceable Standards

Standard Description (Tied to Prior Cores) Enforcement
Purity Default Functions are pure unless at boundaries (Module 07); no globals/mutations. Ruff + project lint/review rule (no global writes in core/), reviews
Immutability Inv Public APIs (api/ or all) return immutable views (tuple/frozenset); internals may mutate but not leak (Module 01/05). Ruff B006 (mutable defaults), type checks, reviews
Composition Law Chains use pipes/combinators where they aid reasoning; loops OK for clarity/perf (Module 03/06). If >3 transforms or lambdas, name intermediates. Review checklists
Algebraic Contract Law Monoids declare associativity/commutativity; sinks declare idempotence (Core 7, Module 05). Hypothesis properties
Error Policy Law Policies (drop/collect/fail_fast) consistent across stages; no implicit try/except (Module 04/06). Reviews, tests
Spec Reproducibility Law Specs pin versions; reconstructions match originals (Core 9). Round-trip tests

These code-level standards ensure enforceable FP quality.


2. Decision Table

Scenario Priority Recommended
Error-heavy code High Introduce Result first (Module 04)
Config sprawl Medium Standardize Reader/partials (Module 06)
Effect tangles High Ports/adapters before async (Module 07/08)
Distributed needs Medium Specs after core purity (Core 9)
Choose based on pain points; always pilot.

3. Public API (No New Code; Templates for Standards)

No runtime API; instead, templates for docs. Use Markdown/Sphinx for standards.

Repo alignment note (end-of-Module-09): - Standards live in course-book/reference/fp-standards.md. - Review checklist lives in course-book/reference/review-checklist.md.

# FP Python Coding Standards (v1.0)

## Purity (Module 01/07)
- Default to pure: explicit inputs/outputs.
- Effects at ports/adapters only.

## Immutability (Module 01/05)
- Public APIs (api/__all__): immutable (frozen dataclass/tuple).
- Internals: mutable OK if contained.

## Composition (Module 03/06)
- Prefer combinators for reasoning; loops for clarity/perf.
- Heuristic: If chain >3 transforms or has lambdas, name intermediates.

## Typing
- mypy --strict.
- Generics with TypeVar.

## Testing
- Properties for laws (Hypothesis).
- 80% coverage; mocks for effects.

## Algebraic Contracts (Module 05)
- Monoids: declare + test assoc/commute.
- Sinks: declare idempotence.

## Error Policies (Module 04/06)
- Explicit Result; consistent across pipes.

## Specs (Core 9)
- Version pinning; reproducibility tests.

4. Reference Implementations

4.1 Approved Patterns + Red Flags

## Patterns
- Config: Reader/partial (Core 1, Module 06).
  Example: `def build_pipe(cfg: Config) -> Callable[[Stream], Stream]: ...`
- Errors: Monadic Result (Module 04/06).
  Example: `result_and_then(load, validate, process)`
- Streams: Generators + itertools (Module 03).
  Example: `pipe(gen_docs, ffilter(pred), fmap(transform))`
- Boundaries: Ports/adapters (Module 07).
  Example: `class StoragePort(Protocol): ...; def file_adapter() -> StoragePort: ...`
- Effects: IoAction/AsyncAction (Module 07/08).
  Example: `action = embedder.embed_batch(items); result = perform_io(action)`
- Idempotence: Keyed ops (Module 08).
  Example: `Keyed(key=stable_hash(c), value=c)`  # Use sha256 or blake2 for stable keys
- Policies: Error combinators (Module 04).
  Example: `filter_ok(stream)` or `collect_both(stream)`

## Red Flags
- Globals: Replace with explicit deps.
- Mutations: Use replace/copy.
- Implicit errors: Use Result.
- Inconsistent policies: Align drop/collect.

4.2 Review Checklist

# FP Code Review Checklist

- [ ] Purity: No hidden state? Explicit deps? (Module 07)
- [ ] Immutability: Immutable public APIs? No leaks? (Module 05)
- [ ] Composition: Combinators aid reasoning? No lambda trains? (Module 06)
- [ ] Types: mypy clean? Generics? (Module 01)
- [ ] Algebraic: Monoids tested for laws? Sinks idempotent? (Module 05)
- [ ] Errors: Policies consistent? No try/except spaghetti? (Module 04)
- [ ] Specs: Versions pinned? Reproducible? (Core 9)
- [ ] Determinism: No races in async/distributed? (Module 08)
- [ ] Tests: Properties for laws? Determinism? (Module 10 Core 4)
- [ ] Docs: Contracts? Pattern refs?
- [ ] Perf: Profiled hotspots? (Module 10 Core 2)

4.3 Adoption/Rollout Playbook

1. Workshop: Cover key cores (1-3 days).
2. Pilot: Refactor RAG module; measure bugs/velocity.
3. Standards: Draft + iterate via team vote.
4. Tools: Pre-commit (ruff/mypy); CI Hypothesis.
5. Reviews: Mandate checklist; pair on disputes.
6. Measure: Retros; track wins (e.g., fewer races).

4.4 Automation Bundle

  • pre-commit: ruff (B006 mutable defaults), mypy --strict.
  • CI: Hypothesis suite; coverage >80%.
  • Ruff config: Enable rules like B006; project lint/review rule enforced by review/CI for no-try-except in core.

4.5 RAG Integration

Enforce in funcpipe-rag-09: Core pure; boundaries explicit; properties for monoids/errors.

4.6 Before/After Refactor

# Before: Mixed
def process(docs):  # Mutation
    for d in docs: d.title = d.title.upper()
    return docs


# After: Standard FP
from dataclasses import replace
from typing import Iterator
from funcpipe_rag import RawDoc, CleanDoc


def process(docs: Iterator[RawDoc]) -> Iterator[CleanDoc]:
    return (CleanDoc.from_raw(replace(d, title=d.title.upper())) for d in
            docs)  # Comprehension for clarity; assume from_raw

5. Property-Based Proofs (tests/test_adoption.py)

from hypothesis import given
from .strategies import doc_strat

@given(docs=doc_strat())
def test_process_idempotent(docs):
    # Law: Repeated calls same (pure)
    res1 = list(process(iter(docs)))
    res2 = list(process(iter(docs)))  # Fresh iter
    assert res1 == res2

@given(docs=doc_strat())
def test_process_immutable(docs):
    orig_titles = [d.title for d in docs]
    _ = list(process(iter(docs)))  # Consume
    assert [d.title for d in docs] == orig_titles  # Inputs unchanged

6. Runtime Preservation Guarantee

Standards add review overhead; FP patterns preserve or improve perf via immutability/caching.

7. Anti-Patterns & Immediate Fixes

Anti-Pattern Symptom Fix
Resistance to change Slow adoption Pilots + wins demos
Inconsistent styles Review disputes Enforce standards doc
Over-abstraction Unreadable code Review for readability
Ignoring pragmatics Perf issues Allow measured escapes
---
## 8. Pre-Core Quiz
1. Standards for…? → Consistency
2. Patterns like…? → Pipe/monads
3. Reviews check…? → Purity/types
4. Adoption via…? → Gradual pilots
5. Benefit? → Maintainable FP
## 9. Post-Core Exercise
1. Draft standards for your team.
2. Review a module with checklist.
3. Pilot FP refactor; measure wins.
Pipeline Usage (Idiomatic)
# Standards-compliant
from toolz import pipe  # Optional from Core 2
result = pipe(docs, clean, chunk, embed)  # Composed, pure

End of Module 09.