When changing code that affects a public interface (method signatures, CLI command arguments, plugin discovery, type/subscriptable behavior), treat it as an API contract: keep existing call patterns working, and ensure runtime semantics match the intended contract.

Practical rules:

Example (signature stability):

class EagerResult:
    # Keep legacy parameters in the signature even if the internal behavior no longer needs them.
    def get(self, timeout=None, propagate=True, disable_sync_subtasks=True, **kwargs):
        if self.successful():
            return self.result
        if self.state in states.PROPAGATE_STATES:
            if propagate:
                raise self.result if isinstance(self.result, Exception) else Exception(self.result)
            return self.result

Example (CLI input contract parsing):

def revoke_by_stamped_header(state, header, terminate=False, signal=None, **kwargs):
    # Support either a list of headers or a list containing header=value pairs.
    headers = header if isinstance(header, list) else [header]
    parsed = []
    for h in headers:
        if '=' in h:
            k, v = h.split('=', 1)
            parsed.append((k, v))
        else:
            parsed.append(h)
    # ... use `parsed` with clear semantics

Add/adjust unit tests to lock in the contract (signature compatibility, parsing shapes, and runtime return types).