DEV Community

Dakota Huang
Dakota Huang

Posted on

Seal File, Clock, and Print Before One Module Split

Split a god module only after tests seal current behavior. Pin files, clocks, prints, and errors before any move. Then extract one pure function and stop there.

Why unsealed splits drift

A wide module hides parse, math, and disk writes together. A quick extract often changes paths, times, or blank lines. Those diffs look tiny and still break callers.

Reviewers cannot separate a safe move from a contract change. Characterization tests record what the code does today. They do not claim that behavior is the right product.

The only change this pass allows

This pass may move one function boundary in one file. Inputs stay identical, and output bytes stay stable. Clocks, prints, and file writes remain in the caller.

Do not rename public functions in the same commit. Do not add a dependency to clean up formatting. Do not fix a known bug until the seal is green.

What to pin, leave, or block

Use this table before you touch the module. A blocked row means the split is not the smallest safe change.

Observed signal Pin now Leave alone Block the split
Summary file bytes Yes
Printed status line Yes
Return value Yes
Exception type on a bad row Yes
File absent after a bad row Yes
Helper names inside the file Yes
Comment text Yes
New third-party package Yes
Clock read moved into the helper Yes
Path built from cwd inside the helper Yes

Step 1: Keep the messy module unchanged

Treat the file below as the current production shape. It is a proposed example, not a measured repo result. Do not improve it before the tests exist.

import datetime
from pathlib import Path

def run_job(source_text, dest_path):
    rows = []
    for line in source_text.splitlines():
        if not line.strip():
            continue
        name, qty, price = line.split(",")
        rows.append((name.strip(), int(qty), float(price)))
    total = 0.0
    lines = []
    for name, qty, price in rows:
        amount = qty * price
        total += amount
        lines.append(f"{name}:{amount:.2f}")
    stamp = datetime.datetime.now().strftime("%Y-%m-%d")
    body = "\n".join(lines + [f"TOTAL:{total:.2f}", stamp])
    Path(dest_path).write_text(body + "\n", encoding="utf-8")
    print(f"wrote {len(rows)} rows")
    return total
Enter fullscreen mode Exit fullscreen mode

Three side effects sit beside the row math. The clock is local and the print uses stdout. The write uses only the destination path given.

Step 2: Seal the happy path

Put the test next to the module in a fresh tree. Freeze the date so a laptop clock cannot change the file. Assert bytes, return value, and the exact print.

import datetime as dt

import job_report

class FrozenDate(dt.datetime):
    @classmethod
    def now(cls, tz=None):
        return cls(2026, 10, 9)

def _freeze(monkeypatch):
    monkeypatch.setattr(job_report.datetime, "datetime", FrozenDate)

def test_happy_path_seals_bytes_and_print(tmp_path, capsys, monkeypatch):
    _freeze(monkeypatch)
    dest = tmp_path / "out.txt"
    source = "Ada, 2, 3.5\n\nBea, 1, 4\n"
    total = job_report.run_job(source, dest)
    assert total == 11.0
    assert capsys.readouterr().out == "wrote 2 rows\n"
    assert dest.read_text(encoding="utf-8") == (
        "Ada:7.00\nBea:4.00\nTOTAL:11.00\n2026-10-09\n"
    )
Enter fullscreen mode Exit fullscreen mode

The fixture date is a lock, not a news claim. The token 2026-10-09 is the review date for this draft. Change it only if your sealed sample used another day.

Step 3: Seal the empty path and the bad row

Empty input still writes a total line and a stamp. A bad quantity raises ValueError before any write. Import pytest before you add the two tests below.

import pytest

def test_blank_lines_only_write_zero_total(tmp_path, capsys, monkeypatch):
    _freeze(monkeypatch)
    dest = tmp_path / "out.txt"
    total = job_report.run_job("\n  \n", dest)
    assert total == 0.0
    assert capsys.readouterr().out == "wrote 0 rows\n"
    assert dest.read_text(encoding="utf-8") == "TOTAL:0.00\n2026-10-09\n"

def test_bad_qty_raises_and_skips_the_file(tmp_path, monkeypatch):
    _freeze(monkeypatch)
    dest = tmp_path / "out.txt"
    with pytest.raises(ValueError):
        job_report.run_job("Ada, x, 3\n", dest)
    assert dest.exists() is False
Enter fullscreen mode Exit fullscreen mode

Both facts are current behavior, even if a later ticket changes them. Do not assert a friendlier error message in this pass. Record the exception type the module raises today.

Step 4: Run the suite on a clean tree

Run these commands from a new virtual environment. They are the proposed workflow, not a published benchmark. Stop if the seal is red, and do not edit production code yet.

python -m venv .venv
source .venv/bin/activate
python -m pip install pytest
python -m pytest tests/test_job_report.py -q
Enter fullscreen mode Exit fullscreen mode

On Windows, use the Scripts activate script instead. Keep the same pytest invocation after that activation. A green run means the seal matches this tree only.

Commit the tests alone if history allows a test-only commit. The next commit may contain only the extract. Two commits make a reverted move easy to see.

Step 5: Extract one pure function

After the seal is green, move only the row math. Leave parsing, the clock, the print, and the write in run_job. The public signature stays run_job with source text and dest path.

def summarize_rows(rows):
    total = 0.0
    lines = []
    for name, qty, price in rows:
        amount = qty * price
        total += amount
        lines.append(f"{name}:{amount:.2f}")
    return lines, total

def run_job(source_text, dest_path):
    rows = []
    for line in source_text.splitlines():
        if not line.strip():
            continue
        name, qty, price = line.split(",")
        rows.append((name.strip(), int(qty), float(price)))
    lines, total = summarize_rows(rows)
    stamp = datetime.datetime.now().strftime("%Y-%m-%d")
    body = "\n".join(lines + [f"TOTAL:{total:.2f}", stamp])
    Path(dest_path).write_text(body + "\n", encoding="utf-8")
    print(f"wrote {len(rows)} rows")
    return total
Enter fullscreen mode Exit fullscreen mode

Re-run the same pytest command with no test edits. A failure means the extract changed a sealed output. Revert the helper and try a smaller move.

Step 6: Reject diffs that fail the table

Read the diff before you trust the green suite. A green suite can miss a new import or a moved clock. Use the block column as a hard stop.

Reject the change if summarize_rows calls datetime or print. Reject it if the helper opens dest_path or reads the working directory. Reject a format tweak, even when the tests still pass by luck.

Add a direct unit check only after the seal stays green. That check may call summarize_rows with in-memory tuples. It must not replace the file and print seals.

def test_summarize_rows_is_pure_math():
    lines, total = job_report.summarize_rows([("Ada", 2, 3.5)])
    assert lines == ["Ada:7.00"]
    assert total == 7.0
Enter fullscreen mode Exit fullscreen mode

This extra test is optional and stays local. Skip it if the extract is not in the tree yet. Do not let it become the only proof.

Where draft help and a remote run fit

A human still owns every sealed assertion here. A model can propose extra cases from the current file. Accept a case only when it matches observed output.

Disclosure: This article was prepared as part of MonkeyCode's product outreach. MonkeyCode's free model access can draft those candidate cases. Its free server option can run the same pytest command.

That split keeps a laptop clock and a home path out of the baseline. Do not treat a drafted assertion as a specification. Compare each expected string with a local run first.

If the server path differs, keep tmp_path and avoid a home directory. Check MonkeyCode's free server option if a remote baseline helps.

Limits of this seal

The seal preserves bugs as well as useful contracts. A wrong total stays wrong until a later separate change. Say that clearly in the pull request body.

Patching datetime depends on the import style in the module. This example patches import datetime, as written above. A from-import style needs a different patch target.

These tests do not cover concurrent writers or partial disk failures. They also skip encoding errors from the destination filesystem. Add those only if production already depends on them.

A free model draft may fix the bad-row path while sealing it. That is a failed draft, not a better refactor. Delete any assertion the current module does not already satisfy.

Two-decimal formatting can hide binary float noise later. Pick sample prices that land on exact tenths.

A hosted runner is not a permanence promise or hardware claim. Confirm current access in the product before you rely on it.

Who should not use this pass

Skip this pass if the ticket requires a behavior change now. Write the new contract test first, then change code. Do not freeze a bug you must remove in the same commit.

Skip it when the module calls production networks or reads real secrets. A characterization run would repeat that side effect. Isolate or replace that call before you record outputs.

Skip it if nobody can run the suite before merge. An unread model draft is not a seal. Also skip it when the next change must cross several packages at once.

Close on the commit boundary

Green tests plus a one-function diff is the exit bar. If the diff also moves the clock, the pass is not done. Revert the diff, reseal the outputs, and extract less.

Top comments (0)