Documentation Conventions¶
This guide explains how to keep documentation synchronized with the codebase and avoid disconnected code examples.
Problem: Disconnected Documentation¶
Documentation that contains pseudo-code or example snippets that don't match the actual implementation creates several problems:
- Misleading users who try to use the examples
- Maintenance burden when code changes but docs don't
- Loss of trust in documentation accuracy
Solution: Linked Documentation¶
We use several techniques to keep documentation synchronized with code.
1. Source Code References¶
Always include links to actual implementation when showing conceptual code:
!!! info "Conceptual Logic"
The following illustrates the logic. For the actual implementation,
see [`classifier.py:285-392`](https://github.com/OpenAfterHours/rwa_calculator/blob/master/src/rwa_calc/engine/classifier.py#L285-L392).
Format for GitHub links:
https://github.com/OpenAfterHours/rwa_calculator/blob/master/src/rwa_calc/engine/classifier.py#L285-L392
2. Embedded Code Snippets¶
Use pymdownx.snippets to embed actual code from source files.
Syntax:
The snippet marker looks like scissors: two dashes, the digit 8, a less-than sign, and two more dashes (- - 8 < - -), followed by a quoted file path.
| Pattern | Description |
|---|---|
Marker + "path/to/file.py" |
Include entire file |
Marker + "path/to/file.py:10:50" |
Include lines 10-50 |
Path is relative to repository root. See pymdownx.snippets docs for full syntax.
Example - the actual snippets in retail.md look like:
See retail.md for working example
Compute all flags that depend only on raw input columns.
Uses two .with_columns() batches: the first pre-computes shared
intermediates (uppercase strings, entity-type mapping) that the
second batch references, avoiding redundant str.to_uppercase()
and replace_strict() calls.
Sets: exposure_class_sa, exposure_class_irb, exposure_class, is_mortgage,
is_defaulted, exposure_class_for_sa, is_infrastructure,
qualifies_as_retail, retail_threshold_exclusion_applied
"""
max_retail_exposure = float(config.retail_thresholds.max_exposure_threshold)
# SL override: exposures with sl_type (from specialised_lending join) get
# SPECIALISED_LENDING class regardless of counterparty entity_type.
sl_override = pl.col("sl_type").is_not_null()
# Batch 1: Pre-compute shared intermediates to avoid redundant work.
# - _sa_class: entity type → SA class mapping (used 3× below)
# - _irb_class: entity type → IRB class mapping
# - _pt_upper: product_type uppercased (used in is_mortgage, infrastructure)
exposures = exposures.with_columns(
[
pl.col("cp_entity_type")
.replace_strict(ENTITY_TYPE_TO_SA_CLASS, default=ExposureClass.OTHER.value)
.alias("_sa_class"),
3. Auto-generated API Documentation¶
Use mkdocstrings to generate documentation from docstrings:
::: rwa_calc.engine.classifier.ExposureClassifier
options:
show_root_heading: true
members:
- classify
show_source: false
This automatically pulls docstrings and keeps them in sync with code.
4. Admonitions for Pseudo-code¶
When pseudo-code is necessary for conceptual explanation, mark it clearly:
!!! info "Conceptual Logic"
The following is simplified pseudo-code to illustrate the concept.
```python
# This is conceptual - see actual implementation below
def simplified_example():
pass
### 5. Collapsible Actual Code
Use collapsible sections to show lengthy actual code without cluttering the page:
??? example "See this working example from hierarchy.py"
```python
config: CalculationConfig,
) -> ResolvedHierarchyBundle:
"""
Resolve all hierarchies and return enriched data.
Args:
data: Raw data bundle from loader
config: Calculation configuration
Returns:
ResolvedHierarchyBundle with hierarchy metadata added
"""
errors: list[CalculationError] = []
counterparty_lookup, cp_errors = self._build_counterparty_lookup(
data.counterparties,
data.org_mappings,
data.ratings,
)
errors.extend(cp_errors)
exposures, exp_errors = self._unify_exposures(
data.loans,
data.contingents,
data.facilities,
data.facility_mappings,
counterparty_lookup,
)
errors.extend(exp_errors)
# Apply FX conversion so threshold calculations use consistent currency
fx_converter = FXConverter()
collateral = data.collateral
guarantees = data.guarantees
provisions = data.provisions
equity_exposures = data.equity_exposures
if config.apply_fx_conversion and data.fx_rates is not None:
exposures = fx_converter.convert_exposures(exposures, data.fx_rates, config)
if collateral is not None:
collateral = fx_converter.convert_collateral(collateral, data.fx_rates, config)
```
## Best Practices
### When Writing New Documentation
1. **Start with the actual code** - Read the implementation first
2. **Use mkdocstrings for API docs** - Let docstrings be the source of truth
3. **Link, don't copy** - Use snippets rather than copying code
4. **Mark pseudo-code clearly** - Use admonitions to indicate conceptual code
### When Updating Code
1. **Check for documentation** - Search for references to your function/class
2. **Update line numbers** - Snippet references may need adjustment
3. **Update docstrings** - mkdocstrings will pull these automatically
4. **Run docs locally** - `zensical serve` to verify everything renders
### Documentation Structure
| Documentation Type | Approach |
|-------------------|----------|
| API Reference | Use `::: module.Class` for auto-generation |
| Conceptual Guides | Pseudo-code with source links |
| Tutorials | Embedded snippets from actual code |
| Examples | Working code from test files |
## Available Tools
### pymdownx.snippets
Configuration in `zensical.toml`:
```toml
[project.markdown_extensions.pymdownx.snippets]
base_path = ["."]
check_paths = false
mkdocstrings¶
Configuration in zensical.toml:
[project.plugins.mkdocstrings.handlers.python.options]
docstring_style = "google"
show_source = true
show_root_heading = true
members_order = "source"
Admonitions¶
Available types: note, info, tip, warning, danger, example
Checking Documentation¶
Before submitting changes:
- Run
zensical servelocally - Check that snippets render correctly
- Verify GitHub links point to correct lines
- Ensure mkdocstrings generates expected output
Migration Checklist¶
When finding disconnected code in documentation:
- [ ] Identify the actual implementation
- [ ] Add source code reference link
- [ ] Convert to embedded snippet if appropriate
- [ ] Add admonition if keeping conceptual code
- [ ] Update line references after code changes