DEV Community

Dakota Huang
Dakota Huang

Posted on

Record the Hot Path, Then Extract One Seam

Messy modules do not need a grand rewrite. They need a lock on observed inputs and outputs. Extract one seam only after that lock stays green.

Coding agents often treat current output as accidental noise. Brownfield branches encode money, retries, and odd quirks. Those quirks are the real product contract today.

Cheap generated diffs raise coupling, not clarity. One unbounded patch can break ten silent callers. A trace file is smaller than a redesign note. It is also executable.

Rank heat before you record

Do not start from architecture opinions or layer diagrams. Start from calls that already happen in smoke tests. Rank functions by inbound references times runtime hits. Touch only the top rank.

Follow this order. Skip a step and the later extract lies.

  1. List candidate modules with a boring import scan.
  2. Count inbound references for each public function.
  3. Pair that count with runtime samples when logs exist.
  4. Pick one function. Never pick a whole package.
# Proposal commands. Not a published benchmark.
rg -n "from ledger import |import ledger" --type py | wc -l
rg -n "def settle_batch\(" --type py
Enter fullscreen mode Exit fullscreen mode

Stop if two functions look equally hot. Split the ranking work first. Dual targets destroy the later seam. Dual targets also poison the lockfile.

Wrap one function with a recorder

The recorder is the artifact. It serializes arguments, kwargs, and results. It appends JSONL. It must not change return values.

This listing is a worked template. It is not production telemetry. Label it as unexecuted until you run it locally.

# recorder.py — characterization wrapper (worked example)
from __future__ import annotations

import hashlib
import json
from pathlib import Path
from typing import Any, Callable

LOCK = Path("behavior.lock.jsonl")


def _dump(value: Any) -> Any:
    if isinstance(value, (str, int, float, bool)) or value is None:
        return value
    if isinstance(value, (list, tuple)):
        return [_dump(item) for item in value]
    if isinstance(value, dict):
        return {str(key): _dump(item) for key, item in sorted(value.items())}
    return {"repr": repr(value), "type": type(value).__name__}


def characterize(fn: Callable) -> Callable:
    def wrapped(*args: Any, **kwargs: Any):
        result = fn(*args, **kwargs)
        record = {
            "fn": fn.__qualname__,
            "args": _dump(args),
            "kwargs": _dump(kwargs),
            "result": _dump(result),
        }
        payload = json.dumps(record, sort_keys=True)
        record["sha256"] = hashlib.sha256(payload.encode()).hexdigest()
        with LOCK.open("a", encoding="utf-8") as handle:
            handle.write(json.dumps(record, sort_keys=True) + "\n")
        return result

    wrapped.__name__ = fn.__name__
    wrapped.__qualname__ = fn.__qualname__
    return wrapped
Enter fullscreen mode Exit fullscreen mode

Wrap only the ranked function. Leave neighbors untouched.

# ledger.py — messy target (illustrative)
from recorder import characterize

@characterize
def settle_batch(rows: list[dict], cutoff_days: int = 30) -> dict:
    kept = [row for row in rows if int(row.get("age", 0)) <= cutoff_days]
    total = sum(int(row.get("cents", 0)) for row in kept)
    flags = {row["id"] for row in kept if row.get("retry")}
    return {"count": len(kept), "cents": total, "retries": sorted(flags)}
Enter fullscreen mode Exit fullscreen mode

Run existing tests or a thin script next. Do not invent new fixtures yet. You want production-shaped rows. Synthetic rows hide the odd branches.

pytest tests/test_ledger_smoke.py -q
wc -l behavior.lock.jsonl
Enter fullscreen mode Exit fullscreen mode

Cap the file after the smoke path finishes. Duplicate traces waste review time. Hash de-duplication can wait one iteration. Redact secrets before the wrapper opens.

# Fail closed if a token-shaped field appears.
rg -n "api_key|password|secret|token" behavior.lock.jsonl && exit 1
Enter fullscreen mode Exit fullscreen mode

Promote traces to closed tests

Each JSONL line becomes one assertion. Missing keys fail. Extra keys fail. List order is part of the contract. Document any allowed sort before you change it.

# test_characterize_settle_batch.py
from __future__ import annotations

import json
from pathlib import Path

from ledger import settle_batch

LOCK = Path("behavior.lock.jsonl")


def _cases():
    for line in LOCK.read_text(encoding="utf-8").splitlines():
        rec = json.loads(line)
        if rec.get("fn") == "settle_batch":
            yield rec


def test_settle_batch_matches_lockfile():
    assert LOCK.exists(), "record the hot path before extracting"
    seen = 0
    for rec in _cases():
        out = settle_batch(*rec["args"], **rec["kwargs"])
        assert out == rec["result"]
        seen += 1
    assert seen >= 3, "need at least three distinct traces"
Enter fullscreen mode Exit fullscreen mode

Three traces is a floor, not a study. Add more when branches disagree. Do not average results. Characterization is exact replay. Commit the lockfile with the replay test together.

Read a mismatch before you "fix" it

A red test is data. It is not a license to edit goldens. Use this debug order.

  1. Print the failing sha256 and the function name.
  2. Diff only result keys. Ignore wrapper metadata.
  3. Check argument coercion, especially int() on strings.
  4. Revert the extract if the public dict shifted.
  5. Patch the lockfile last, and only for intended product changes.
pytest tests/test_characterize_settle_batch.py -q
python -c "import json; print(json.loads(open('behavior.lock.jsonl').readline())['sha256'])"
Enter fullscreen mode Exit fullscreen mode

Agents like to soothe the assertion. Reject that patch. The lockfile is the caller. The helper is the suspect.

Extract one seam, nothing else

Keep the public function signature frozen. Move one private helper. Re-run the lockfile tests. If they fail, revert. Do not reshape the test to match the helper.

def _keep(rows: list[dict], cutoff_days: int) -> list[dict]:
    return [row for row in rows if int(row.get("age", 0)) <= cutoff_days]


def settle_batch(rows: list[dict], cutoff_days: int = 30) -> dict:
    kept = _keep(rows, cutoff_days)
    total = sum(int(row.get("cents", 0)) for row in kept)
    flags = {row["id"] for row in kept if row.get("retry")}
    return {"count": len(kept), "cents": total, "retries": sorted(flags)}
Enter fullscreen mode Exit fullscreen mode

That is the whole refactor. No rename campaign. No new package. One helper whose name matches one verb. Stop if readability does not improve.

Decision table

Signal Action Stop condition
Observed traces under three Record more smoke paths Still under three after one full run
Traces disagree on key names Store both shapes as cases Do not merge schemas
Extract changes a public return Revert the extract Never patch the lockfile first
Function talks to network or clock Wrap those seams first Do not record live time
Agent diff touches two files Reject the diff One seam, one file
Arguments contain secrets Redact or abort Lockfile must stay non-sensitive

Where a free coding host fits

Drafting the wrapper is boilerplate. Running the suite should not depend on a dirty laptop. Disclosure: This article was prepared as part of MonkeyCode's product outreach. MonkeyCode provides free model access and a free server option. Use the models to draft recorder.py and the replay test. Use the free server option to run pytest against the lockfile. Keep the lockfile in git. Generated text is not the contract. The JSONL file is.

Do not ask any model to invent traces. Invented traces encode a prior, not your repo. Feed the ranked function only. Ask for the wrapper. Then you record. Then you extract.

Limitations

This method preserves behavior. It does not improve behavior. If the hot path is wrong, characterization gold-plates the bug. Do not treat the lockfile as a product spec.

JSON serialization drops object identity. Live sockets and ORM instances need adapters. Without adapters the lockfile stores repr strings. Those strings are brittle across versions.

Nondeterministic code will flap. Seeds, clocks, and network calls need seams first. If you cannot seam them, stop. A flaky lockfile is worse than no tests.

Who should not use this

Skip this workflow on greenfield modules with real unit tests. Skip it when the goal is a behavior change. Skip it when no smoke path exists. Skip it for security-critical parsers that need property tests.

Teams chasing a full rewrite will hate the pace. The point is the pace. One seam per green suite. The lockfile is the review artifact. The helper is optional.

Checklist

  1. Rank one function by fan-in and runtime heat.
  2. Wrap it with the JSONL recorder.
  3. Run existing smoke paths until three traces exist.
  4. Commit behavior.lock.jsonl with the replay test.
  5. Extract one private helper in the same file.
  6. Re-run. Revert on mismatch. Do not soothe the goldens.

If the extract does not pay for itself in reading time, keep the lock and stop. The next agent diff now has a closed oracle. That is the whole point.

Top comments (0)