The Work
Before and after: real IceCream source code
Left shows the actual source from gruns/icecream on GitHub. Right shows DocPilot's AI-generated Google-style docstrings.
icecream/icecream.py — configureOutput()
def configureOutput(self, prefix=_absent, outputFunction=_absent, argToStringFunction=_absent, includeContext=_absent, contextAbsPath=_absent): if prefix is not _absent: self.prefix = prefix if outputFunction is not _absent: self.outputFunction = outputFunction if argToStringFunction is not _absent: self.argToStringFunction = \ argToStringFunction if includeContext is not _absent: self.includeContext = includeContext if contextAbsPath is not _absent: self.contextAbsPath = contextAbsPath
def configureOutput(self, prefix=_absent, outputFunction=_absent, argToStringFunction=_absent, includeContext=_absent, contextAbsPath=_absent): """Configure IceCream's output behavior. Customizes how ic() formats and displays debug output. All parameters are optional; only provided values are updated. Args: prefix: String or callable prepended to output. Default: 'ic| '. outputFunction: Callable that handles the formatted string. Default: stderr. argToStringFunction: Callable to convert values to strings. Default: pprint.pformat. includeContext: If True, include filename and line number in output. contextAbsPath: If True, show absolute file paths instead of relative. Example: ic.configureOutput(prefix='DBG| ') ic.configureOutput(includeContext=True) """
_absent) and runtime behavior since the source has no type annotations. Each parameter description stays under two lines to match the library's concise character.icecream/icecream.py — __call__() — the ic() function
def __call__(self, *args): if self.enabled: callFrame = inspect.currentframe() \ .f_back try: out = self._formatArgs( callFrame, inspect.currentframe() .f_code.co_filename, args) except: out = self._formatArgs( callFrame, '', args) self.outputFunction(out) if not args: passthrough = None elif len(args) == 1: passthrough = args[0] else: passthrough = args return passthrough
def __call__(self, *args): """Debug-print arguments with context. The primary interface for IceCream. With arguments, prints each alongside its source expression and value. Without arguments, prints the filename and line number. Args: *args: Values to inspect. Each is printed with its source expression. Returns: None if no args, the single value if one arg, or a tuple if multiple args. Example: >>> ic(my_var) ic| my_var: 42 >>> result = ic(compute(x)) ic| compute(x): 7 >>> ic() ic| script.py:12 in main() """
__call__ returns None, a single value, or a tuple depending on argument count. We documented all three cases explicitly because this passthrough behaviour is IceCream's key feature and was completely undocumented.icecream/builtins.py — install(), enable(), disable(), format()
def install(name='ic'): builtins = __import__('builtins') setattr(builtins, name, ic) def enable(): ic.enabled = True def disable(): ic.enabled = False def format(*args): return ic.format(*args)
def install(name='ic'): """Install ic() as a global builtin. Makes ic() available in every module without importing. Args: name: Builtin name. Default: 'ic'. """ builtins = __import__('builtins') setattr(builtins, name, ic) def enable(): """Enable ic() output globally.""" ic.enabled = True def disable(): """Disable ic() output globally. ic() calls still return their arguments but produce no printed output. """ ic.enabled = False def format(*args): """Format args as ic() would, without printing. Returns: Formatted debug string. """ return ic.format(*args)
Results and Impact
What this means in real numbers
Time and Cost Comparison
What the team gets
Every new function added to the codebase is automatically checked for docstring coverage. Every PR runs through prose linting, coverage enforcement, and AI review before merge. Documentation stays in sync with code because the pipeline catches drift on every commit — not at the end of a sprint when context is lost.
Generated Output
Auto-generated API reference
mkdocstrings renders this directly from the new docstrings. Zero manual writing required.
class IceCreamDebugger
The core debugging class. Instantiated as the global ic object.
Configure IceCream's output behavior. All parameters are optional.
| Parameter | Type | Default | Description |
|---|---|---|---|
prefix | str | Callable | 'ic| ' | Prepended to output |
outputFunction | Callable | stderr | Handles the formatted string |
argToStringFunction | Callable | pprint.pformat | Converts values to strings |
includeContext | bool | False | Include file and line in output |
contextAbsPath | bool | False | Show absolute file paths |
Install ic() as a global builtin, making it available in all modules without importing.
Toggle debug output globally. When disabled, ic() calls still return their arguments but produce no output.
Format arguments as ic() would, returning the string instead of printing it.
The Problem
A beloved library with no API docs
IceCream has 10,000+ GitHub stars, 70M+ PyPI downloads, and active maintenance through 2026 — yet its documentation situation was remarkably poor.
What was missing from the codebase
| Area | Status |
|---|---|
| Docstrings | Zero — no functions documented |
| API reference site | None — no Sphinx, MkDocs, or ReadTheDocs |
| Type documentation | None — 5 kwargs with no type info |
| Parameter descriptions | None — users rely on README examples |
docs/ folder | Missing — does not exist in the repository |
The Pipeline
4 automated quality gates on every PR
DocPilot's CI/CD validates prose quality, docstring coverage, tests, and documentation build integrity before any merge.
AI Review
3 CrewAI agents validated every docstring
A sequential pipeline of specialized agents checked accuracy, completeness, and style compliance before documentation was merged.
Generated Google-style docstrings from AST analysis of IceCream's source code
Cross-referenced every docstring against actual code for type and parameter accuracy
Verified Google Developer Docs Style Guide compliance at 94%
3 minor style suggestions: summary period, blank line formatting, Returns section for __init__
The Framework
8 tools in one agile documentation pipeline
The Process
Documentation integrated into every sprint
Definition of Done
Every user story requires docstrings written, Vale passing, coverage at or above 80%, MkDocs building in strict mode, and a tech writer review scheduled within two sprints.
RACI Matrix
Developers write first drafts. Technical writers review and polish. Team leads ensure process adherence. CI/CD enforces quality floors automatically.
Sprint Allocation
Reserve 15–20% of sprint capacity for documentation: feature docs bundled with user stories, plus quarterly doc-debt reduction sprints.