schmonz.com is a Fediverse instance that uses the ActivityPub protocol. In other words, users at this host can communicate with people that use software like Mastodon, Pleroma, Friendica, etc. all around the world.
This server runs the snac software and there is no automatic sign-up process.
Characterization Tests First, Then the Smallest Safe Change
Stop refactoring. Start recording. The smallest safe change wins only if you can prove nothing else moved.
That is the whole method. You freeze the hidden inputs, snapshot the current output, then change one line. The snapshot decides whether you were safe.
This article walks through a runnable loop on a small legacy stand-in module. Swap in your real module and the steps stay the same.
The ticket here is vague: unknown PLAN breaks invoice generation. Do not translate that into an architecture plan yet.
Translate it into one observable statement. "Unknown plan raises KeyError before any row is processed."
Now the statement is testable. It also tells you the bug lives in one lookup, not in the whole loop.
Legacy modules read two hidden inputs constantly: the clock and the environment. Both change between your laptop and CI.
Here is the stand-in under test. It is deliberately small and deliberately dirty.
# billing/cycle.py
import datetime
import os
RATES = {"standard": 1.0, "pro": 0.8, "legacy": 1.25}
def invoice_lines(rows):
today = datetime.date.today()
plan = os.environ.get("PLAN", "standard")
rate = RATES
[plan] out =
[] for row in rows:
days = (today - row["start"]).days
if days < 0:
days = 0
amount = round(row["units"] * rate * days, 2)
out.append({"id": row["id"], "days": days, "amount": amount})
return out
Two hidden inputs sit in four lines. datetime.date.today() reads the machine clock. os.environ.get reads the process environment.
Freeze both by patching the names inside the module under test. Never patch the stdlib module globally.
# tests/test_cycle_char.py
import datetime
import json
import os
import pathlib
import types
import pytest
from billing import cycle
GOLDEN = pathlib.Path(__file__).parent / "golden" / "invoice_lines.json"
class FixedDate(datetime.date):
@classmethod
def today(cls):
return cls(2026, 3, 1)
@pytest.fixture
def frozen(monkeypatch):
monkeypatch.setenv("PLAN", "pro")
monkeypatch.setattr(cycle, "datetime", types.SimpleNamespace(date=FixedDate))
This works because the module calls datetime.date.today() by attribute access. A module that does from datetime import date needs a different patch point.
That detail matters. Note it, or you will fight a passing test that proves nothing.
Write the rows once, run them, and save the output. The recorder is the source of truth, not your memory of the old behavior.
ROWS = [
{"id": "a1", "start": datetime.date(2026, 2, 1), "units": 3},
{"id": "a2", "start": datetime.date(2026, 3, 4), "units": 2},
{"id": "a3", "start": datetime.date(2026, 3, 1), "units": 0},
]
def test_invoice_lines_matches_golden(frozen):
got = cycle.invoice_lines(ROWS)
if os.environ.get("RECORD_GOLDEN") == "1":
GOLDEN.parent.mkdir(parents=True, exist_ok=True)
GOLDEN.write_text(json.dumps(got, indent=2))
assert got == json.loads(GOLDEN.read_text())
Record once with RECORD_GOLDEN=1 python -m pytest tests/test_cycle_char.py -q. The first run always passes, and that is expected.
Read the recorded file by hand before you commit it. A snapshot is not a truth claim.
[
{"id": "a1", "days": 28, "amount": 67.2},
{"id": "a2", "days": 0, "amount": 0.0},
{"id": "a3", "days": 0, "amount": 0.0}
]
Pin the error path too. Empty input still hits the lookup, so the exception fires before the loop.
def test_unknown_plan_raises_keyerror(monkeypatch, frozen):
monkeypatch.setenv("PLAN", "platinum")
with pytest.raises(KeyError):
cycle.invoice_lines([])
A test that cannot fail is decoration. Break a copy of the code and confirm the suite goes red.
rsync -a --exclude .git ./ /tmp/char-mut/
sed -i 's/if days < 0:/if days < -1:/' /tmp/char-mut/billing/cycle.py
cd /tmp/char-mut && python -m pytest tests/test_cycle_char.py -q; echo "exit=$?"
Expect exit=1. The clamped future date now leaks a negative days value, and the golden catches it.
If the suite still passes, your cases do not cover the branch. Fix that before touching the real module.
Smallest here is one expression. Replace the strict lookup with a guarded one.
- rate = RATES
[plan]+ rate = RATES.get(plan, RATES["standard"])
This changes observable behavior, so the pinned error test must change with it. Edit it deliberately, in the same commit.
-def test_unknown_plan_raises_keyerror(monkeypatch, frozen):
- monkeypatch.setenv("PLAN", "platinum")
- with pytest.raises(KeyError):
- cycle.invoice_lines([])
+def test_unknown_plan_falls_back_to_standard(monkeypatch, frozen):
+ monkeypatch.setenv("PLAN", "platinum")
+ assert cycle.invoice_lines([]) ==
[]Now the diff shows one intended behavior edit. Everything else stays green, which is the only proof you have.
Use Python 3.11 or newer here. Python 3.9 is already past its end-of-life date, so new test tooling should not target it.
Rank candidate changes by blast radius. Take rank 0 first, then climb one rung per commit.
Rank
Change
Lines touched
Gate to pass
0
Rename a local variable
1
goldens unchanged
1
Add a guard clause
1-3
goldens unchanged
2
Extract one pure helper
5-15
goldens unchanged, call order identical
3
Change observable behavior
1-5
exactly one pinned test edited on purpose
4
Move the module
many
old import path still pinned
5
Rewrite the module
all
stop, split into ranks 0-4
Rank 3 is the one people skip. They mix a behavior fix with a structure change and lose the ability to review either.
Set stop rules before you start. Seam hunting expands without limits otherwise.
Each stop is a signal to shrink scope, not to lower standards.
Drafting the edge-case rows in step 3 is the slow part. That is the part worth delegating.
I use MonkeyCode's free model access to propose candidate input rows and hidden-global guesses. Disclosure: This article was prepared as part of MonkeyCode's product outreach. The free server option lets me run the mutation check on a repo copy without provisioning a machine of my own.
The model proposes. The recorder decides. Never let a model write the expected output from memory.
Free access and a free server option are the only two availability claims I make. I have not measured throughput or uptime, and a free tier is not an SLA.
Characterization tests pin current behavior, including bugs. They do not prove correctness and they do not replace intent-based tests.
Exact JSON equality is brittle with floats. Compare amounts with pytest.approx when your golden is hand-edited.
Clock patching only works when the module reads time by attribute access. Import styles, C extensions, and third-party clients may refuse the patch.
Skip this method if you are writing greenfield code, deleting the module next sprint, or have no way to execute it. Skip it if your team cannot review a behavior diff honestly.
If you run this loop on a repo copy, start with the recorder and keep it in charge of the truth.#testing #python #refactoring #legacycode #software #coding #development #engineering #inclusive #community
Characterization Tests First, Then the Smallest Safe Change
Mixed None-and-raise APIs fail under model rewrites. The error taxonomy is the real product surface. Pin that taxonomy with characterization tests first, always.
Change one except clause only after the pin stays green. Happy-path unit tests will not save this refactor. Callers already branch on None, dict keys, and types.
Collapse any one of those shapes and production breaks. The defect is an unrecorded taxonomy, not missing types.
AI coding threads keep offering full-file cleanups. Those cleanups prefer one error type. They also prefer raising over returning None.
That preference is style, not evidence. A messy dispatcher often has three outcomes. Some inputs raise ValueError for bad payloads.
Some inputs return None after a send failure. Some inputs return {"ok": False, "status": 429}. Downstream code already checks all three shapes.
Loop-style agent edits make the same cut. They unify handlers because duplication looks sloppy. Duplication here is the published contract.
Record these four fields for every fixture. Skip prose messages on the first pass. Message strings drift across harmless edits.
None, mapping keys, or other.Types and keys usually stay stable. Status integers also stay stable. Log counts need a named logger, not the root.
The table below is the spec. It is a labeled example. It is not a production trace.
Fixture
Escapes
Return
Status
WARN+
empty body
RuntimeError
n/a
n/a
0
invalid JSON
ValueError
n/a
n/a
0
JSON list
ValueError
n/a
n/a
0
missing id
none
None
n/a
1
send TypeError
none
None
n/a
1
send TimeoutError
none
None
n/a
1
downstream 429
none
dict
429
1
downstream 200
none
dict
200
0
Keep the send TypeError row. That row is the trap. A cleaner except often drops it.
The code is a worked example. Run it locally before any extract. Do not treat it as measured field data.
# events.py — labeled example, not a live service
from __future__ import annotations
import json
import logging
from typing import Any, Callable
log = logging.getLogger("events")
SendFn = Callable[[dict], tuple[int, Any]]
def dispatch_event(raw: str | None, send: SendFn):
"""Messy contract: None, dict, or raise."""
if raw is None or raw == "":
raise RuntimeError("empty body")
try:
payload = json.loads(raw)
except Exception:
raise ValueError("bad json")
if not isinstance(payload, dict):
raise ValueError("bad json")
if "id" not in payload:
log.warning("missing id")
return None
try:
status, body = send(payload)
except Exception:
log.warning("send failed")
return None
if status >= 400:
log.warning("downstream %s", status)
return {"ok": False, "status": status, "body": body}
return {"ok": True, "status": status, "body": body}
# test_events_contract.py — worked example
import json
import logging
import pytest
from events import dispatch_event
def _records(caplog):
return [r for r in caplog.records if r.name == "events" and r.levelno >= logging.WARNING]
def _run(raw, send, caplog):
caplog.set_level(logging.WARNING, logger="events")
try:
value = dispatch_event(raw, send)
return None, value, _records(caplog)
except Exception as exc:
return type(exc), None, _records(caplog)
def test_empty_body_raises_runtime_error(caplog):
exc, value, recs = _run("", lambda p: (200, "ok"), caplog)
assert exc is RuntimeError
assert value is None
assert len(recs) == 0
@pytest.mark.parametrize("raw", ["{", "[]", "null"])
def test_bad_json_raises_value_error(raw, caplog):
exc, value, recs = _run(raw, lambda p: (200, "ok"), caplog)
assert exc is ValueError
assert value is None
assert len(recs) == 0
def test_missing_id_returns_none(caplog):
exc, value, recs = _run('{"name": "x"}', lambda p: (200, "ok"), caplog)
assert exc is None
assert value is None
assert len(recs) == 1
def test_send_type_error_returns_none(caplog):
def send(_payload):
raise TypeError("broken client")
exc, value, recs = _run('{"id": 1}', send, caplog)
assert exc is None
assert value is None
assert len(recs) == 1
def test_send_timeout_returns_none(caplog):
def send(_payload):
raise TimeoutError("late")
exc, value, recs = _run('{"id": 1}', send, caplog)
assert exc is None
assert value is None
assert len(recs) == 1
def test_downstream_429_is_dict(caplog):
exc, value, recs = _run('{"id": 1}', lambda p: (429, "slow"), caplog)
assert exc is None
assert value == {"ok": False, "status": 429, "body": "slow"}
assert len(recs) == 1
def test_downstream_200_is_dict(caplog):
exc, value, recs = _run('{"id": 1}', lambda p: (200, {"n": 1}), caplog)
assert exc is None
assert value == {"ok": True, "status": 200, "body": {"n": 1}}
assert len(recs) == 0
Run the file before editing handlers. Use a short traceback during the first pin.
python -m pytest test_events_contract.py -q --tb=short
A green suite with no mutation check is weak. You must see a failed unified-error fork. Keep that fork out of the merge.
# test_events_mutation.py — must fail against a unified raise rewrite
import events
def test_unified_raise_would_break_send_type_error(monkeypatch, caplog):
def rewritten(raw, send):
payload = __import__("json").loads(raw)
status, body = send(payload) # TypeError now escapes
return {"ok": status < 400, "status": status, "body": body}
monkeypatch.setattr(events, "dispatch_event", rewritten)
with pytest.raises(TypeError):
events.dispatch_event('{"id": 1}', lambda p: (_ for _ in ()).throw(TypeError("x")))
Expect this mutation test to fail on the rewrite. Restore the messy handler after that check. The failure is the evidence, not a vibe.
is None or catch types.except edit.Step two is not optional. Tests invented from the callee miss caller branches. Caller branches are the contract.
rg -n "dispatch_event\(" -g "*.py"
rg -n "is None|except ValueError|except RuntimeError" -g "*.py"
Record each hit as a fixture name. Missing hits become missing rows. Missing rows make unsafe extracts look safe.
Do not narrow except Exception on send first. The table says TypeError becomes None. Narrowing that clause raises TypeError instead.
That raise is a contract break. Extract the block without narrowing. Keep the same log line and None return.
def _send_or_none(payload, send):
try:
return send(payload)
except Exception:
log.warning("send failed")
return None
def dispatch_event(raw, send):
if raw is None or raw == "":
raise RuntimeError("empty body")
payload = _parse_object(raw)
if "id" not in payload:
log.warning("missing id")
return None
result = _send_or_none(payload, send)
if result is None:
return None
status, body = result
if status >= 400:
log.warning("downstream %s", status)
return {"ok": False, "status": status, "body": body}
return {"ok": True, "status": status, "body": body}
That extract is boring. Boring is the point. The taxonomy stays in the table.
A later change can narrow exceptions. Do that only with a version note. Add a row that expects TypeError to escape. Tell callers before you merge.
The JSON branch is different. json.loads should raise json.JSONDecodeError. Re-raising ValueError is the published type.
Narrowing except Exception to except json.JSONDecodeError can be safe. Prove it with the invalid JSON row. Prove it with the JSON list row too.
def _parse_object(raw):
try:
payload = json.loads(raw)
except json.JSONDecodeError:
raise ValueError("bad json") from None
if not isinstance(payload, dict):
raise ValueError("bad json")
return payload
Both bad-JSON fixtures must still raise ValueError. A leaked JSONDecodeError is a taxonomy change. Do not ship that leak without a caller audit.
from None also drops __cause__. Add a cause fixture if any caller reads it. Skip that fixture when no caller inspects causes.
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
A model can draft the extract. It must not invent a new taxonomy. MonkeyCode provides free model access and a free server option.
Use either only after the characterization file is green. Feed the model the table and the test file. Ask for one extract, not a rewrite.
Reject a patch that removes a row. Reject a patch that changes None into a raise. This is not an agent loop.
One prompt. One diff. Same tests.
The harness does not prove semantic equality. It pins types, keys, status, and log counts. It misses timing, retry storms, and byte identity.
Log counts break if a library logs extra warnings. Pin the logger name events. Cap propagation on that logger.
Do not pin the root logger. Root pins go red on unrelated imports. That noise hides a real taxonomy drift.
The table is only as good as the fixtures. One unlisted caller path is an untested shape. Untested shapes are how "safe" extracts land in incident channels.
Do not use this flow for greenfield APIs. Design one error shape there. Do not preserve None plus raises on purpose.
Do not use this flow for security boundaries. Characterization will pin insecure behavior. Pinning is not hardening.
Do not use this flow without tests you can run offline. A model server cannot replace the table. If pytest cannot collect locally, stop.
Teams with a published OpenAPI error schema may skip dict-key rows. Use the schema as the table instead. Still pin process-local exception escapes.
HTTP schemas often omit those local raises. Omitting them is how ValueError turns into a 500. Keep the escape column anyway.
except.None callers still exist, or a version note ships.If a model patch violates any line, drop the patch. Rewrite pressure is not evidence. The table is.
If the characterization file is already green, one extract prompt on the free server is enough. Skip that prompt when the table still has empty cells.#python #testing #refactoring #ai #software #coding #development #engineering #inclusive #community
Freeze the Error Contract Before One except Change
Do not extract file helpers from a messy module yet. Snapshot every resolved absolute path before the first edit. Relative opens form a hidden contract across three roots.
Most failed extracts start as a path-root mismatch. The helper looks cleaner after the extract lands. The files then land in a different directory tree.
A typical messy module mixes three path roots. None of them appear in the public function signature.
os.getcwd() follows the process, not the source file. Pytest, systemd, and cron each change that value.
Path(__file__).resolve() follows the module on disk. A later package move silently retargets every relative open.
An env var such as DATA_DIR may override both. Empty, relative, and absolute values all behave differently.
The public function still accepts only a basename. Callers believe the output path stays stable. That belief does not survive a chdir.
The listing below is a teaching example, not production code. It writes one report beside three different roots.
# report_kit.py — messy on purpose
from __future__ import annotations
import json
import os
from pathlib import Path
HERE = Path(__file__).resolve().parent
def write_daily_report(name: str) -> dict[str, str]:
data_dir = os.environ.get("DATA_DIR", "data")
cwd_out = Path("out") / name
here_out = HERE / "out" / name
env_out = Path(data_dir) / name
payload = {"name": name, "pid": os.getpid()}
text = json.dumps(payload, sort_keys=True) + "\n"
cwd_out.parent.mkdir(parents=True, exist_ok=True)
here_out.parent.mkdir(parents=True, exist_ok=True)
env_out.parent.mkdir(parents=True, exist_ok=True)
cwd_out.write_text(text, encoding="utf-8")
here_out.write_text(text, encoding="utf-8")
env_out.write_text(text, encoding="utf-8")
return {
"cwd": str(cwd_out),
"here": str(here_out),
"env": str(env_out),
}
A naive extract wraps the three write_text calls. It often introduces Path.cwd() in one place. One root then silently absorbs the other two.
Return values still look like relative strings. Tests that assert those strings stay green. The bytes move anyway.
Build a ledger before any helper extract. Record caller, raw argument, and resolved absolute path. Hash the sorted ledger. Treat that hash as a characterization oracle.
# path_ledger.py — teaching harness
from __future__ import annotations
import hashlib
import json
import os
import traceback
from pathlib import Path
LEDGER: list[dict[str, str]] =
[]def _caller() -> str:
frames = traceback.extract_stack()
for frame in reversed(frames[:-1]):
if "path_ledger.py" not in frame.filename:
return f"{frame.filename}:{frame.lineno}:{frame.name}"
return "unknown"
def record(kind: str, raw: str, resolved: Path) -> None:
LEDGER.append(
{
"kind": kind,
"caller": _caller(),
"raw": raw,
"cwd": os.getcwd(),
"resolved": str(resolved.resolve()),
}
)
def ledger_hash() -> str:
blob = json.dumps(LEDGER, sort_keys=True, indent=2)
return hashlib.sha256(blob.encode("utf-8")).hexdigest()
def dump(path: Path) -> str:
path.write_text(
json.dumps(LEDGER, indent=2, sort_keys=True) + "\n",
encoding="utf-8",
)
return ledger_hash()
Wrap writes at the test boundary only. Do not patch production code for this measurement. Run the same invocation the module already trusts.
# test_path_ledger.py — characterization, not a unit test
from __future__ import annotations
from pathlib import Path
from unittest.mock import patch
import path_ledger
import report_kit
GOLDEN = Path(__file__).parent / "goldens" / "report_kit_paths.json"
GOLDEN_HASH = Path(__file__).parent / "goldens" / "report_kit_paths.sha256"
def _traced_write_text(self: Path, *args, **kwargs):
path_ledger.record("Path.write_text", str(self), self)
return Path.write_text(self, *args, **kwargs)
def test_write_daily_report_path_ledger(tmp_path, monkeypatch):
monkeypatch.chdir(tmp_path)
monkeypatch.setenv("DATA_DIR", str(tmp_path / "env-data"))
monkeypatch.setattr(report_kit, "HERE", tmp_path / "pkg")
(tmp_path / "pkg").mkdir()
with patch.object(Path, "write_text", _traced_write_text):
report_kit.write_daily_report("daily.json")
digest = path_ledger.dump(tmp_path / "ledger.json")
if not GOLDEN.exists():
GOLDEN.parent.mkdir(parents=True, exist_ok=True)
GOLDEN.write_text(
(tmp_path / "ledger.json").read_text(encoding="utf-8"),
encoding="utf-8",
)
GOLDEN_HASH.write_text(digest + "\n", encoding="utf-8")
raise AssertionError("golden created; rerun to pin")
assert digest == GOLDEN_HASH.read_text(encoding="utf-8").strip()
Label this pin as an oracle, not as coverage. The first run writes goldens on purpose. The second run fails on any resolved-path drift.
Trace mkdir in the same harness when directories matter. A helper can reuse a folder the original code created. That reuse still retargets later writes.
Use the table before any patch is accepted. Each row is a veto, not a preference.
Signal in the ledger
Safe extract?
Required pin
cwd-relative out/name
Not yet
chdir plus expected absolute
__file__-relative out/name
Not yet
frozen HERE
env-relative DATA_DIR/name
Not yet
empty, relative, absolute env
mixed roots in one function
No
split by root, not call shape
only basenames change
Yes
hash still matches
A model often groups the three writes together. They share write_text, so the grouping looks obvious. The ledger groups them by root instead.
Root grouping is the correct split. Call-shape grouping is the usual defect.
Follow these steps in order and skip none.
open, write_text, mkdir, and Path constructor you might move.DATA_DIR, absolute DATA_DIR.Relative goldens will lie after a machine change. Absolute goldens survive a different checkout path. That is the entire point of the pin.
Commands for the first pin:
mkdir -p goldens
python -m pytest test_path_ledger.py -q
# first run creates goldens and fails
python -m pytest test_path_ledger.py -q
# second run must pass before any extract
Commands after a proposed extract:
python -m pytest test_path_ledger.py -q
git diff -- goldens/report_kit_paths.json
# any resolved-path line change is a rejected patch
A smoking-gun diff looks like this fragment:
- "resolved": "/tmp/pytest-of-dev/test0/out/daily.json"
+ "resolved": "/home/ci/project/out/daily.json"
The source diff can still look like a tidy helper. The ledger line is the reject signal.
The smallest change moves one root. It does not introduce a generic writer yet.
def _write_cwd_report(name: str, payload: str) -> str:
target = Path("out") / name
target.parent.mkdir(parents=True, exist_ok=True)
target.write_text(payload, encoding="utf-8")
return str(target)
Leave __file__ and DATA_DIR writes in the original function. A later extract can take the second root. Each extract must keep the ledger hash unchanged.
Do not normalize paths inside the new helper. Normalization is a behavior change, not cleanup. Record it as a new ledger when you truly need it.
Avoid .resolve() in the extracted helper. resolve() follows symlinks and can rewrite goldens. Prefer the same construction the messy module already used.
open() and Path modules
Some messy modules still call open() directly. Trace that path with a thin wrapper. Keep the same ledger schema for both styles.
import builtins
from pathlib import Path
_real_open = builtins.open
def traced_open(file, mode="r", *args, **kwargs):
raw = str(file)
path_ledger.record("open", raw, Path(file))
return _real_open(file, mode, *args, **kwargs)
Install the wrapper only inside the characterization test. Production code should keep its original open. The oracle lives in the test process.
If both open and Path.write_text appear, keep both kinds. Dropping one kind hides a root. Hidden roots are how extracts ship broken CI writes.
A free coding model can draft the one-root helper. It cannot choose the root. The ledger already chose.
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
MonkeyCode provides free model access and a free server option. Use the server to run the four pinned invocations. Feed the model the messy function, the ledger JSON, and the decision table. Reject any patch that alters a resolved field.
The model is a diff generator in this workflow. The hash remains the reviewer for every patch. Do not skip that reviewer for a cleaner diff.
Hash changes but the source looks equivalent. A Path constructor started calling .resolve() early. Absolute strings then diverge on symlinks.
Hash stays stable on a laptop and fails in CI. The golden cwd was never isolated. Rerun the pin under tmp_path.
Only two of three writes appear in the ledger. mkdir created a directory the extract later reuses. Trace mkdir as a first-class event.
Env values look absolute in every golden row. The test set an absolute DATA_DIR only. Add the unset and relative cases before review.
Returned relative strings still match after the extract. Callers never saw the absolute target. Assert the ledger, not the return map.
This ledger does not prove functional correctness of the report. File contents can still rot under this pin. Pair it with a payload hash when bytes matter.
It misses networked I/O by design. HTTP and object-store clients need a different oracle. Do not reuse this hash for those calls.
It is weak against symlink farms in deploy trees. resolve() follows links; absolute() does not. Pick one rule and keep it fixed.
Race conditions remain outside the ledger. Two processes can share one cwd. The ledger is per-process and will not serialize them.
Windows drive letters and UNC paths need extra goldens. Do not copy a POSIX hash onto Windows runners. Split those hashes by platform.
Do not use this workflow on a greenfield module. Write explicit path arguments first in new code. There is nothing useful to characterize there.
Do not use it when output locations must change on purpose. Update the golden in the same commit as the move. Do not treat the hash as sacred then.
Do not use it as a substitute for backup policy. Characterization does not recover overwritten files. Keep real backups for destructive jobs.
Skip it for one-off notebooks and scratch CLIs. The process cwd is the product in those tools. A ledger mostly adds noise there.
.resolve() calls on write paths.DATA_DIR cases include unset, relative, and absolute values.If any box is still open, keep the helper on the branch. Ship the path pin first. The extract can wait for a green hash.
Run the ledger on a free server if you already have one. Keep the extract behind that green hash.#python #testing #refactoring #ai #software #coding #development #engineering #inclusive #community
Characterize Path Resolution Before You Move One open()
We Let AI Agents Rewrite a 92M-Message-a-Day Service in Go—Zero Incidents, by (not on Mastodon or Bluesky):
https://www.checklyhq.com/blog/agentic-rewrite-nodejs-to-go/?ref=frontenddogma.com
Simplifying and Refactoring Introductory Calculus
https://arxiv.org/abs/1811.03459
Comments: https://news.ycombinator.com/item?id=49306196
#HackerNews #calculus #education #refactoring #math #simplification #arxiv
A Short Guide to Naming by Tim Ottinger is free with a Leanpub Reader membership! Or you can buy it for $6.50! https://leanpub.com/naming_shortguide #computer_programming #python #javascript #java #web_development #refactoring
Habr » 🤖 🌐
@habr@zhub.link
[Перевод] Экономическая выгода рефакторинга в эпоху AI-агентов
Осваивая разработку с помощью AI-агентов, я написал веб-приложение для собственной ежедневной работы. Проект получился довольно сложным: с динамическим обновлением интерфейса и поиском, модальными окнами, автосохранением, интеграциями с внешними системами, модулями машинного обучения, текстовым анализом, фоновыми задачами и автоматическим деплоем. Объём кода составил около 150 000 строк, из которых примерно 120 000 написаны на Rust, а остальные - на TypeScript и Terraform. Весь этот код сгенерировали агенты - в основном Claude Code и частично Cursor . За редкими исключениями я почти не открывал и не читал исходные файлы. В процессе разработки я начал замечать странности. Когда в терминале мелькнула правка 4000-й строки в одном файле, я решил посмотреть на код ближе. Выяснилось, что слой доступа к данным разросся до 6000 строк. С каждой новой функцией он продолжал расти. В коде каждого запроса, чтения или записи повторялись настройка HTTP-запроса, кодирование и декодирование JSON. В итоге весь слой доступа к данным оказался в одном файле на 17 155 строк Rust.
https://habr.com/ru/articles/1065178/
#refactoring #ииагенты #ai #рефакторинг #разработка_приложений #разработка #software_engineering #software_architecture #бюджет #code_review
How we migrated a live routing system using #AI-assisted #refactoring https://www.datadoghq.com/blog/engineering/ai-assisted-storage-migration/
Rewriting Bun in Rust, by @jarredsumner (@bun.sh):