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
¶
# 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
¶
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) | ||
End of Module 09.