Docstring
A docstring (short for documentation string) is a string literal in Python that occurs as the first statement in a module, function, class, or method definition, serving as built-in documentation for that object and becoming its __doc__ special attribute.[1][2] These docstrings provide a concise summary of the object's purpose, usage, parameters, return values, and potential exceptions, enhancing code readability and maintainability for developers.[1][2]
Docstrings are recommended for all public modules, functions, classes, and methods in Python code, including the __init__ method of classes and package initialization files (__init__.py), but are optional for private or trivial elements.[1] They can be accessed programmatically via the __doc__ attribute or displayed interactively using the built-in help() function, which extracts and formats the docstring for user-friendly output.[2][3] For example, in a function definition:
python
def example_function(param1, param2):
"""Return the sum of two numbers.
Args:
param1 (int): The first number.
param2 (int): The second number.
Returns:
int: The sum of param1 and param2.
"""
return param1 + param2
def example_function(param1, param2):
"""Return the sum of two numbers.
Args:
param1 (int): The first number.
param2 (int): The second number.
Returns:
int: The sum of param1 and param2.
"""
return param1 + param2
Calling help(example_function) would then show the docstring content.[2]
The structure and content of docstrings are standardized by PEP 257, which emphasizes clarity and consistency without prescribing low-level formatting details.[1] One-line docstrings are single sentences enclosed in triple double quotes ("""), starting with a verb in imperative mood, ending with a period, and fitting on one line for simple cases.[1] Multi-line docstrings begin with a one-line summary, followed by a blank line, then detailed sections on arguments, returns, side effects, and exceptions, with the closing quotes on a dedicated line and indentation stripped relative to the opening quotes.[1] Scripts may include additional usage messages as module docstrings to guide command-line invocation.[1]
Beyond PEP 257's foundational guidelines, several extended formats have emerged for more structured documentation, particularly in scientific and large-scale projects.[1] The Google Python Style Guide builds on these by mandating sections like Args:, Returns:, and Raises: with colon-separated parameter descriptions and consistent indentation, making docstrings suitable for automated tools like Sphinx.[4] Similarly, the NumPy docstring format, designed for array-oriented libraries, incorporates reStructuredText elements with ordered sections such as Parameters, Returns, Notes, See Also, and Examples, supporting doctests and mathematical notation for enhanced API reference generation.[5] These conventions are enforced by linters like pydocstyle and integrated into documentation generators, promoting best practices across the Python ecosystem.[1][3]
Overview
Definition
A docstring, short for documentation string, is a string literal placed within the source code of a module, function, class, or method to document its purpose, behavior, arguments, return values, and potential side effects.[1] In programming languages that support this convention, such as Python, the docstring serves as an integral part of the code structure, providing human-readable explanations that can be programmatically accessed.[1]
Key characteristics of a docstring include its placement immediately following the opening statement of the documented element, such as after the def keyword for a function or class for a class definition.[1] It supports both single-line and multi-line formats, with multi-line docstrings typically enclosed in triple quotes to allow for detailed descriptions across multiple lines.[1] Through language introspection mechanisms, like the __doc__ attribute in Python, docstrings become accessible as attributes of the associated object, enabling runtime inspection and automated documentation generation.[1]
Unlike inline comments, which are non-executable annotations ignored by the interpreter and intended solely for developers reading the source code, docstrings are executable string literals designed for extraction and external use.[6] This distinction ensures that docstrings contribute to the code's metadata rather than being discarded during execution.[7]
For example, a basic single-line docstring might appear as:
python
def add(a, b):
"""Returns the sum of two numbers."""
return a + b
def add(a, b):
"""Returns the sum of two numbers."""
return a + b
A multi-line docstring could be formatted as:
python
def multiply(numbers):
"""
Multiplies all numbers in a list.
Args:
numbers (list): A list of numeric values.
Returns:
int or float: The product of all numbers.
"""
result = 1
for num in numbers:
result *= num
return result
def multiply(numbers):
"""
Multiplies all numbers in a list.
Args:
numbers (list): A list of numeric values.
Returns:
int or float: The product of all numbers.
"""
result = 1
for num in numbers:
result *= num
return result
These formats facilitate clear, structured documentation that enhances code maintainability and supports tools for automated documentation.[1]
Purpose and Benefits
Docstrings primarily serve to create self-documenting code by embedding descriptive strings directly within modules, functions, classes, and methods, offering immediate insight into their intended functionality and usage.[8] This approach allows developers to comprehend the purpose and behavior of code components without delving into the full implementation, streamlining comprehension during development and maintenance.[8] Additionally, docstrings facilitate the automated generation of application programming interfaces (APIs) and external documentation, enabling tools to extract and format this information for user-facing resources.[3]
The benefits of docstrings extend to enhancing overall code readability and maintainability, as standardized descriptions promote consistency across projects and reduce the cognitive load on readers.[1] They foster collaboration among team members by providing clear, accessible explanations that bridge gaps in understanding, particularly in large codebases. Through runtime accessibility via reflection—such as the inspect.getdoc() function—docstrings enable dynamic retrieval of documentation, supporting interactive help systems and programmatic analysis.[9] By explicitly specifying inputs, outputs, side effects, and exceptions, docstrings help minimize errors arising from misunderstandings of interface expectations.[1]
In software engineering practices, docstrings integrate seamlessly with testing frameworks like doctest, which parses embedded examples within docstrings to verify their correctness, ensuring that documentation remains executable and up-to-date.[10] This alignment supports documentation-driven development, where clear specifications guide implementation and validation, aligning with principles of literate programming.[10] Such integration reinforces reliable software evolution by tying descriptive content to verifiable behavior.
Historically, docstrings emerged to address the need for embedded, machine-readable documentation in dynamic languages like Python, where traditional header-based approaches are less feasible due to the interpretive nature of the code. Early motivations focused on overcoming inconsistent formats that impeded tool development for extracting and processing documentation, ultimately standardizing practices to bolster code usability and tool compatibility. This evolution, formalized through proposals like PEP 257, underscores their role in promoting disciplined documentation habits within the Python ecosystem.[1]
Conventions and Standards
General Guidelines
Effective docstrings adhere to key principles of clarity, conciseness, and completeness to ensure they provide understandable guidance for developers without overwhelming detail.[11] Clarity involves using simple, precise language that explains the "why" behind the code's purpose, assuming the reader has no prior context.[11] Conciseness means limiting descriptions to essential information, avoiding redundancy by linking to external resources when appropriate.[12] Completeness requires covering the function's intent, usage, and relevant behaviors, such as edge cases, to facilitate reliable integration.[13] Additionally, employing the imperative mood in descriptions—such as "Returns the computed sum"—promotes direct, actionable phrasing that aligns with how code is used.[13]
A recommended structure for docstrings begins with a one-line summary that captures the core purpose, followed by optional detailed sections for parameters (e.g., Args), return values (e.g., Returns), exceptions (e.g., Raises), and illustrative examples (e.g., Examples).[12] This format organizes information hierarchically, starting with an overview for quick scanning and expanding into specifics for deeper understanding.[11] Parameters should detail inputs' types, roles, and constraints; returns should specify output types and conditions; raises should outline error scenarios and triggers; and examples should demonstrate typical and atypical usage to reinforce comprehension.[13]
Common pitfalls in docstring writing include overly verbose explanations that obscure key points, inconsistent terminology across documents that confuses readers, and omitting coverage of edge cases that leads to unexpected behaviors in production.[11] Excessive detail can dilute focus, while varying terms—like alternating between "input" and "argument"—hinder searchability and maintenance.[12] Failing to address boundary conditions, such as null inputs or overflow risks, undermines the docstring's utility as a reliable reference.[13]
To enhance accessibility, docstrings must be parseable by tools like documentation generators, achieved through consistent indentation—typically aligned with the code block—and standardized quoting conventions that preserve formatting during extraction.[12] This ensures seamless integration with IDEs and build systems, enabling automated help displays and API references without manual reformatting.[11]
Language-Specific Standards
In Python, the foundational standard for docstrings is outlined in PEP 257, created in May 2001, which establishes conventions for writing clear, consistent documentation strings within modules, classes, functions, and methods. This includes guidelines for one-line summaries, multi-line structures with initial summary lines followed by blank lines, and sections detailing arguments, return values, exceptions, and side effects, while recommending avoidance of self-documenting variable names in favor of descriptive prose. PEP 257 focuses on structure without specifying markup syntax.[1]
Building on PEP 257, community-driven variations have emerged for specialized needs. The Google Python Style Guide, published in 2012 and updated periodically, structures docstrings with labeled sections like "Args:" for parameters (e.g., "x: int, The input value") and "Returns:" for output descriptions, prioritizing imperative phrasing and line length limits under 80 characters to improve readability in large codebases. Similarly, the NumPy style guide, introduced around 2013 as part of the SciPy documentation ecosystem, extends reStructuredText with directive-based tags such as ":param name type: Description" for parameters and ":rtype: type" for returns, facilitating automated parsing for scientific computing libraries where type hints and array shapes are critical. These styles maintain PEP 257's core principles but add domain-specific annotations for better tool interoperability.[4][5]
In JavaScript, the JSDoc format serves as the predominant convention since its initial release in 2011, employing special comment blocks delimited by "/** */" to annotate functions, classes, and modules with tags like "@param {type} name - Description" for inputs and "@returns {type} Description" for outputs, supporting type expressions compatible with tools like TypeScript. This approach allows for generating HTML documentation and IDE autocompletion, though it remains non-enforced by the language runtime.[14]
For Java, Javadoc conventions, formalized in the Java Language Specification since JDK 1.0 in 1996 and detailed in Oracle's style guide, utilize "/** */" blocks to document public APIs, incorporating tags such as "@param parameter-name description" and "@return description" to specify behaviors, exceptions, and deprecated elements, with an emphasis on HTML-safe phrasing and ordered tag grouping for clarity. These doc comments are processed by the javadoc tool to produce API references, making them integral to enterprise software documentation despite being optional for compilation.[15]
The evolution of these standards reflects growing emphasis on self-documenting code: PEP 257 was introduced in 2001, around the time of Python 2.2's release, with refinements in PEP 287 (2002) for reStructuredText integration and later alignments in PEP 484 (2014) for type annotations within docstrings; JSDoc matured through versions 3.x starting in 2011 to accommodate ES6+ features; Javadoc has seen iterative updates tied to JDK releases, such as enhanced tag support in JDK 8 (2014). In terms of adoption, Python's ecosystem promotes widespread docstring use via PEP 257 enforcement in linters like pylint and integration with packaging standards, contrasting with statically typed languages where Java's Javadoc is conventional for public APIs but less ubiquitous in private code due to explicit type declarations, and JavaScript's JSDoc varies by project scale, often optional outside typed environments like Deno or TypeScript.[1]
Implementations in Programming Languages
Python
In Python, docstrings are string literals that serve as documentation for modules, functions, classes, and methods, becoming accessible as the __doc__ attribute of the object.[1] They are placed immediately after the definition header, using triple double quotes ("""), and follow conventions outlined in PEP 257 for structure and content.[2] The feature originated in early Python versions and was formalized through PEP 257 in 2002, standardizing their high-level structure without altering the language syntax.[1]
Docstrings can be accessed programmatically via the __doc__ attribute, such as function.__doc__, or displayed interactively using the built-in help() function, which retrieves and formats the docstring for readability.[2] The inspect module provides advanced introspection capabilities, including inspect.getdoc(object) to extract and clean the docstring—removing common indentation and handling inheritance for classes—and inspect.cleandoc() to normalize whitespace.[16] This runtime accessibility enables tools to generate documentation dynamically.
For simple cases, one-line docstrings offer a concise summary directly after the header:
python
def my_function():
"""This function does nothing."""
pass
def my_function():
"""This function does nothing."""
pass
Multi-line docstrings expand on this with a summary line, a blank line, and detailed sections. They typically include subsections like Args for parameters, Returns for output, and Raises for exceptions, each on separate lines for clarity.[1] Indentation is preserved but normalized by tools based on the first non-blank line.
Here is an example of a multi-line function docstring:
python
def complex(real=0.0, imag=0.0):
"""Form a complex number.
Keyword arguments:
real -- the real part (default 0.0)
imag -- the imaginary part (default 0.0)
"""
return Complex(real, imag)
def complex(real=0.0, imag=0.0):
"""Form a complex number.
Keyword arguments:
real -- the real part (default 0.0)
imag -- the imaginary part (default 0.0)
"""
return Complex(real, imag)
Class-level docstrings summarize the class's purpose, list public attributes and methods, and often document the __init__ method separately. For instance:
python
class Complex:
"""Represents a complex number.
Public methods:
__init__(real, imag) -- create a new complex number
__repr__() -- return a string representation
"""
def __init__(self, real, imag):
"""Initialize with real and imaginary parts."""
self.real = real
self.imag = imag
class Complex:
"""Represents a complex number.
Public methods:
__init__(real, imag) -- create a new complex number
__repr__() -- return a string representation
"""
def __init__(self, real, imag):
"""Initialize with real and imaginary parts."""
self.real = real
self.imag = imag
Advanced features include embedding doctests directly in docstrings using interactive Python prompts (>>>) to demonstrate usage and verify behavior via the doctest module, which parses and executes these examples as tests.[10] An example integrates a doctest for a factorial function:
python
def factorial(n):
"""Return the factorial of n, an exact [integer](/page/Integer) >= 0.
If the argument is negative, raises a ValueError.
>>> factorial(5)
120
>>> factorial(-1)
Traceback (most recent call last):
...
ValueError: n must be >= 0
"""
import math
if n < 0:
raise ValueError("n must be >= 0")
if n == 0:
return 1
return math.prod(range(1, n + 1))
def factorial(n):
"""Return the factorial of n, an exact [integer](/page/Integer) >= 0.
If the argument is negative, raises a ValueError.
>>> factorial(5)
120
>>> factorial(-1)
Traceback (most recent call last):
...
ValueError: n must be >= 0
"""
import math
if n < 0:
raise ValueError("n must be >= 0")
if n == 0:
return 1
return math.prod(range(1, n + 1))
Type hints, introduced in PEP 484, complement docstrings by annotating parameter and return types in function signatures (e.g., def func(name: str) -> None:), while docstrings provide narrative details, such as exception descriptions not covered by annotations.[17] Python's ecosystem supports rendering docstrings into formatted documentation using Sphinx, which via its autodoc extension imports modules, extracts docstrings in reStructuredText format, and generates HTML or other outputs, often with extensions like napoleon for Google or NumPy styles.[18]
Other Languages
In various programming languages beyond Python, docstring-like mechanisms exist to embed documentation directly in source code, facilitating both human readability and automated processing. These features often resemble Python's docstrings in purpose but differ in syntax, storage, and accessibility, reflecting the language's design paradigms. For instance, dynamic languages tend to integrate documentation as runtime-accessible metadata, while statically typed or compiled languages prioritize comment-based extraction for build-time documentation generation.[19][20]
In Elixir, a functional language built on the Erlang VM, documentation is provided through module and function attributes rather than string literals. The @moduledoc attribute documents an entire module, while @doc precedes a function or type specification to describe it. These attributes store plain text or Markdown-formatted strings as metadata, which can be introspected at runtime using functions like Code.get_docs/1 or the IEx helper h/1. ExDoc, the standard tool, extracts these attributes during compilation to generate HTML documentation, supporting features like cross-references and source code integration. For example:
defmodule Greeter do
@moduledoc """
A [module](/page/Module) for greeting users.
"""
@doc """
Greets the given name.
## Examples
iex> Greeter.hello("World")
"Hello, World!"
"""
def hello(name), do: "Hello, #{name}!"
end
defmodule Greeter do
@moduledoc """
A [module](/page/Module) for greeting users.
"""
@doc """
Greets the given name.
## Examples
iex> Greeter.hello("World")
"Hello, World!"
"""
def hello(name), do: "Hello, #{name}!"
end
This approach ensures documentation is tightly coupled with code and available in interactive environments.[19]
Common Lisp, an early dynamic language, embeds documentation strings directly within defining forms like defun for functions or defclass for classes. The optional second argument to defun is a string that becomes the function's documentation, stored as symbol metadata and accessible at runtime via the documentation function, which takes a symbol and a documentation type (e.g., :function). This enables introspection in REPLs or lisps like SBCL. Documentation tools such as CLHS or third-party generators parse these strings for formatted output, emphasizing Lisp's tradition of self-documenting code. An example defun form appears as:
(defun square (x)
"Returns the square of X."
(* x x))
(defun square (x)
"Returns the square of X."
(* x x))
Here, (documentation #'square :function) retrieves the string "Returns the square of X." at runtime.
JavaScript, a dynamically typed language, lacks built-in docstrings but relies on JSDoc, a convention using block comments in the /** */ format placed immediately before declarations. These comments include tags like @param, @returns, and @example to structure information, which tools parse to generate API docs or provide IDE hints via static analysis. Unlike true strings, JSDoc comments are not stored in the runtime object model but can be processed by libraries like jsdoc-to-markdown for extraction. Runtime access is indirect, often through reflection in environments like Node.js, but primarily supports tooling rather than introspection. A typical JSDoc-annotated function is:
/**
* Adds two numbers.
* @param {number} a - The first number.
* @param {number} b - The second number.
* @returns {number} The sum of a and b.
* @example
* add(2, 3); // 5
*/
function add(a, b) {
return a + b;
}
/**
* Adds two numbers.
* @param {number} a - The first number.
* @param {number} b - The second number.
* @returns {number} The sum of a and b.
* @example
* add(2, 3); // 5
*/
function add(a, b) {
return a + b;
}
This format promotes type annotations in untyped JavaScript, enhancing developer tools without altering runtime behavior.[14]
Ruby, an object-oriented dynamic language, uses RDoc conventions where comments immediately preceding method or class definitions serve as documentation "strings." These are plain text blocks starting with #, parsed by the RDoc tool to generate HTML or ri-formatted output, supporting markup for sections, examples, and links. Unlike Python's executable strings, Ruby's are non-executable comments not directly accessible at runtime; introspection focuses on method metadata via Method#source_location or ri commands, but doc content requires pre-generation. Yard, an alternative, extends this with more tags but follows similar comment-based extraction. For instance:
# Adds two numbers.
#
# This method performs basic arithmetic addition.
#
# @param a [Integer] The first number.
# @param b [Integer] The second number.
# @return [Integer] The sum.
def add(a, b)
a + b
end
# Adds two numbers.
#
# This method performs basic arithmetic addition.
#
# @param a [Integer] The first number.
# @param b [Integer] The second number.
# @return [Integer] The sum.
def add(a, b)
a + b
end
RDoc processes these for comprehensive API docs, emphasizing Ruby's readable syntax.[21]
In Go, a statically typed compiled language, documentation relies on "godoc" comments—lines starting with // immediately before package, type, function, or variable declarations. These form the first paragraph as a summary, with subsequent paragraphs and preformatted blocks for details, parsed by the go doc command or pkg.go.dev for online HTML generation at compile time. Godoc comments are not runtime strings but source annotations, with limited introspection via the reflect package for declarations, not content. This compile-time focus suits Go's emphasis on fast builds and simple tooling. An example includes:
// Package math provides basic math functions.
package math
// Add returns the sum of x and y.
func Add(x, y int) int {
return x + y
}
// Package math provides basic math functions.
package math
// Add returns the sum of x and y.
func Add(x, y int) int {
return x + y
}
The go doc math Add command extracts and displays the comment.[20]
Key differences among these implementations lie in introspection capabilities and integration. Dynamic languages like Common Lisp and Elixir store documentation as runtime metadata, enabling interactive access (e.g., via documentation or h/1), whereas JavaScript's JSDoc, Ruby's RDoc comments, and Go's godoc are primarily for static extraction by tools during build or analysis phases, with minimal or no runtime availability. This variance aligns with language philosophies: runtime flexibility in interpreted environments versus optimized, generated docs in compiled ones.[20]
Documentation Generators
Documentation generators are tools that extract docstrings from source code and transform them into formatted external documentation, such as HTML pages, PDFs, or other readable formats, facilitating the creation of comprehensive API references and user guides. These tools parse docstrings according to language-specific conventions, like reStructuredText for Python or Javadoc-style tags for Java and JavaScript, and integrate them into structured outputs while supporting cross-references between modules and functions. The generation workflow typically involves configuring the tool via files like conf.py for Python-based generators, scanning source code for docstrings, resolving inheritance and dependencies, and rendering the content with customizable themes.[18][22]
In Python, Sphinx is a prominent documentation generator that uses its autodoc extension to import modules and pull docstrings formatted in reStructuredText, automatically inserting them into documentation files via directives like .. automodule:: or .. autofunction::. Setup involves creating a conf.py file to enable extensions and specify source paths, after which sphinx-build compiles the output into HTML or other formats, handling cross-references through labels and roles for seamless navigation. Sphinx supports theme customization via extensions like sphinx-themes and integrates search functionality using JavaScript-based indexes, making it suitable for large projects. Epydoc, a legacy tool now largely superseded by Sphinx, generates API documentation from Python docstrings using its epytext markup, producing HTML or PDF outputs with strong support for tracing class inheritances and generating call graphs, though it is limited to Python 2 and no longer actively maintained.[23][18][24] MkDocs, paired with the mkdocstrings plugin, enables docstring extraction in Markdown-based projects; the Python handler uses Griffe to parse source code and insert formatted docstrings into Markdown pages via ::: identifier directives, supporting cross-references and rendering to static HTML sites with built-in search and navigation.[25]
For cross-language support, Doxygen processes docstrings in multiple languages including Python, C++, and Java, using special commands like @param or @return within block comments to structure documentation, which it then renders into HTML, LaTeX, or RTF formats with automatic indexing of classes, namespaces, and inheritance diagrams. In JavaScript, JSDoc generates API documentation from inline comments tagged with @param, @returns, or @type, parsing source files to create HTML outputs that include searchable indexes and example code blocks, configurable via a JSON file for templates and plugins. These tools generally follow a parsing phase to extract and validate docstrings against standards like NumPy or Google styles, a linking phase for cross-references via unique identifiers, and a rendering phase that applies CSS themes and generates navigation menus or full-text search via tools like Lunr.js.[26][27][28]
Key features across these generators include auto-indexing of modules and functions for quick lookup, integration of search bars for querying docstring content, and customization of output themes to match project branding, such as Sphinx's Alabaster or Read the Docs themes. For instance, NumPy's documentation is generated using Sphinx with the numpydoc extension, which parses NumPy-style docstrings to produce detailed API references including parameter tables and cross-links to related functions, demonstrating widespread adoption in scientific computing libraries.[29]
Integrated development environments (IDEs) enhance developer productivity by leveraging docstrings for interactive features such as hover tooltips and autocomplete suggestions. In Visual Studio Code (VS Code), the official Python extension uses the Pylance language server to display docstrings in hover tooltips, providing immediate access to function descriptions, parameters, and return types when the cursor is placed over code elements. This integration parses common docstring formats like Google, NumPy, and reStructuredText to render formatted previews, improving code comprehension during editing. Similarly, PyCharm from JetBrains incorporates docstrings into its quick documentation popup, which can be configured to appear on mouse hover via the "Show on Mouse Move" option in the Documentation tool window settings. PyCharm also supports docstring-based autocomplete by inferring types from annotations within docstrings, enabling context-aware suggestions for arguments and return values.
Linters play a crucial role in validating docstring quality and compliance during development. Pydocstyle is a dedicated static analysis tool that enforces PEP 257 conventions by checking for issues such as missing docstrings, improper formatting, and inconsistencies in structure across modules, classes, and functions. It supports conventions like Google and NumPy styles and can be integrated into editor plugins for real-time feedback. For additional validation, pydoc-markdown processes docstrings to generate Markdown output, allowing developers to verify completeness and consistency by inspecting the rendered documentation inline or in previews.
Static analysis tools extend docstring enforcement beyond basic linting to broader code quality assessments. Pylint includes checks for docstring presence and basic adherence, flagging missing module, class, or function docstrings as violations (e.g., C0114 for missing module docstrings), which helps maintain comprehensive documentation standards. While mypy primarily focuses on type checking, it can indirectly leverage docstrings containing type hints for improved static analysis when configured with plugins like mypy-docstring, ensuring that documented types align with code signatures.
At runtime, Python's built-in inspect module provides dynamic access to docstrings through functions like inspect.getdoc(), which retrieves and cleans the documentation string from any object, such as a function or class, by expanding tabs and removing indentation artifacts. This enables interactive help systems, like the built-in help() function, to display docstrings on demand, supporting exploratory programming and debugging workflows.
These tools collectively streamline development by enforcing docstring standards, automating stub generation from docstrings for type inference, and integrating into continuous integration/continuous deployment (CI/CD) pipelines. For instance, pydocstyle and Pylint can be hooked into pre-commit frameworks or CI services like GitHub Actions to fail builds on docstring violations, ensuring consistent documentation quality across team contributions. Interrogate, a coverage tool, quantifies docstring completeness by reporting metrics such as the percentage of documented functions, allowing teams to set thresholds in CI checks for measurable improvements in code maintainability.