Code & Engineering

Safe Refactoring: Refactor Without Changing Behavior

Recommended

Refactors, cleans up and modernizes Python, JavaScript, TypeScript and React code without changing what it does. It guards the change that cleanups make most often without saying so - how the code treats missing and empty values - and ends each answer with a short Behavior section that names what was checked and anything that changed. Use when asked to refactor, clean up, tidy, simplify or modernize a function, class, component or file.

Safe Refactoring: Refactor Without Changing Behavior is a SKILL.md that refactors, cleans up and modernizes Python, JavaScript, TypeScript and React code without changing what it does; an agent buys it once for $0.05 over x402.

Tested 2026-11-13No code, no hidden instructionsv1.0.1 · 9.7 KB · perpetual license

Not for

Changing what code does on purpose, such as a bug fix or a new feature, performance tuning, and moving code between frameworks. It reads the code you paste and does not run your tests, so code with heavy side effects needs a test of its own. Languages other than Python, JavaScript, TypeScript and React get a short note, not full rules.

Tested, honestly

Tested 2026-11-13 with a strong and a weak model.

With and without the skill

Results with and without the skill, for Sonnet and Haiku
SonnetHaiku
withwithoutwithwithout
Runs with no silent empty-value change (10 cases)20/2011/2010/107/10

Same bare request and same scorer on both sides. Read by hand, Sonnet alone named its changes on a CSV export, a React avatar, a retry policy and a settings loader. It missed a discounts function where codes=None became an empty tuple called safe, so passing None raises; a logger and a fetch helper whose null options object now throws; a paginator where a branch it called dead still handled a page size of zero; and one of two runs on a price formatter (strictly 10 of 20, since the other run counted only for a general sentence). A probe with only the name and short description also gave 10 of 10: the gain comes from pointing Sonnet at empty values, and the body added nothing measurable on these cases. Haiku alone missed three: the null logger options, a null avatar name and backoff=None in a retry policy.

Same cases and the same checks with and without the skill. The cases are ours, written around what the skill is for; with a handful of cases, a difference of one or two is within noise.

SonnetStrong model, claude-sonnet-5-5
Right on all 20 runs, scored by running the code it wrote next to the original. In each of the nine cleanup cases it kept the old result for explicit None or null, zero and empty strings, or named the change: a discount list that may arrive as None kept its None check, a logger and a fetch helper kept working with a null options object, and a page size of zero kept its rule. The control was modernized with no difference. Every answer was a real rewrite and ended with a Behavior section.
HaikuWeak model, claude-haiku-5-5
Right on all 10 cases, scored by running the code it wrote next to the original: no empty input changed its result without a line saying so in the Behavior section, the control was modernized with no difference, and each answer rewrote the code rather than leaving it as it was.

Full test summary

Example

Our own test text, before and after the skill ran. Excerpts only.

English · claude-sonnet-5-5

Before

from datetime import datetime DISCOUNTS = { "WELCOME10": {"kind": "percent", "value": 10, "expires": datetime(2027, 1, 1)}, "BLACKFRIDAY": {"kind": "percent", "value": 25, "expires": datetime(2026, 12, 1), "exclusive": True}, "FIVEOFF": {"kind": "fixed", "value": 5, "expires": datetime(2026, 12, 31)}, } def apply_discounts(order, codes=None, now=None): if codes is None:…

After

```python from datetime import datetime DISCOUNTS = { "WELCOME10": {"kind": "percent", "value": 10, "expires": datetime(2027, 1, 1)}, "BLACKFRIDAY": {"kind": "percent", "value": 25, "expires": datetime(2026, 12, 1), "exclusive": True}, "FIVEOFF": {"kind": "fixed", "value": 5, "expires": datetime(2026, 12, 31)}, } def _line_total(line): qty = line.get("qty") if qty is None:…

What is in the file

  • The habit
  • The rules that decide the table
  • Running the table
  • Examples (patterns, not your code)
  • What the answer looks like
  • What stays the same

Languages

Any language. Tried in: English.

License

Perpetual, non-exclusive; use and modify for yourself incl. paid work; no resale or republishing. Holder: Georgi Kalchev, aiskills402.com. Full terms.

Versions

Current version 1.0.1, updated 2026-11-13. Whoever bought an earlier version gets new ones free through the same re-download token.

  1. v1.0.1 · 2026-11-13

    One sentence in "What stays the same" reworded (no rule changed, not re-tested): its five-word ending was the same as a line in a free article on our blog, and paid wording must not appear there.

  2. v1.0.0 · 2026-11-13

    First release. The idea is ours: a probe without a skill on 13 November 2026, where Sonnet, asked only to clean up code, turned an explicit-None sentinel into a parameter default and an `options || {}` line into destructuring, called it safe, and a caller passing None or null then got an error. No foreign skill or repository was opened.

    What the file does: for each rewritten line that decides what a missing or empty value becomes, it puts the left-out argument, explicit None or null, the falsy values and a null options object through the old line and the new one; by default it keeps the old result in modern syntax, otherwise it names each difference. The answer ends with a Behavior section. Python, JavaScript, TypeScript and React, with a short note for PHP, Ruby, Kotlin, C# and Go.

    Test on 13 November 2026: ten cases written by us (nine cleanup requests where a rewrite can quietly change an empty value, one control), each a bare request such as "Clean up this function." with no word about empty values. The rubric was written before the first run. Scored by a script that executes the code from every answer and the original on the same inputs (explicit None or null, 0, "", an empty list, a left-out argument, a null options object) and lists every difference; whether the answer named each difference was read by hand. The scorer was checked both ways: the original against itself gave no difference in 10 of 10 cases, and the four failures recorded in the probe were all caught. The pattern check in cases.json only asks for a non-empty answer, so compare-baseline was not used for the count. No check was widened after the runs.

    Results: Sonnet, two runs per case, 20 of 20 with the file and 11 of 20 without (strictly 10: one bare run on the price formatter was counted right only for a general sentence that defaults cover undefined alone). Haiku, one run per case, 10 of 10 with and 7 of 10 without. With the file the scorer found no difference in any of the 30 answers, and each was a real rewrite (no `var` left, a third to three quarters of the lines new).

    Before release the description was cut from 814 to 440 characters, so that it names the topic and the Behavior section but no longer carries the rule; the side with the file was re-run in full and gave the same 20 of 20 and 10 of 10. A probe with only the name and the short description, without the body, also gave 10 of 10 on Sonnet: on these ten cases the gain comes from pointing the model at empty values, and the body did not add a measurable difference. Harder cases were not measured.

    Price 50000 ($0.05): Sonnet gained 9 runs of 20, which is the top row of the table, but the facts are public language rules, not owner-measured, so the lower tier.

FAQ

Exactly what lines get compared?

Every rewritten line that decides what a missing or empty value turns into: an is None or == null check, a fallback with or, || or ??, a parameter or destructuring default, a dictionary lookup with a fallback, a truthiness test, a tuple() or String() call riding along, and field defaults in a dataclass, pydantic model or TypeScript class. For each it compares the old and new line on a left-out argument, explicit None or null, zero, an empty string or list, and a null options object.

Will it refuse to modernize my code?

No. It does the cleanup you asked for: modern syntax, less repetition, clearer names, smaller functions. Where the tidy form would give another result for an empty input, it keeps the old result in the new syntax, for example a null options object read through ?? before destructuring. If the new result is the better one, it makes the change and names the input, what it gave before and what it gives now.

Does it help Claude Sonnet?

For empty values, clearly. We gave ten bare cleanup requests in Python, JavaScript and React, two runs each, and ran the old and new code on empty inputs. Alone, Sonnet quietly changed an empty value in 9 of 20 runs: None became an empty tuple, a null options object started to throw, a zero page size lost its rule. With the file it did so in none of 20. Haiku went from 7 to 10 of 10. To be fair: the short description alone also gave 10 of 10, so pointing the model at empty values does most of the work; the body added nothing measurable on these cases.

What does the Behavior section say?

When nothing changed, one line naming the empty inputs it compared. Otherwise one line per difference: the input, the old result and the new one, plus the reason when the change was deliberate. An error that became a returned value counts as a difference. It leaves out warnings about inputs the old code did not handle either, so a reviewer reads only real changes.

Read more

Share

Read this page as Markdown: /skills/safe-refactoring.md.

  • Code Review

    Code & Engineering

    SKILL.md · v1.0.4 · 6.0 KB

    Reviews a code change (a diff or a changed file pasted as text) and reports real problems ranked by severity — correctness bugs, security holes, data loss, missing error handling, edge cases, resource leaks — each with location, reason and a concrete fix in words. Use when asked to review code, a diff or a pull request, check a change for bugs, or find security problems in a snippet.

    $0.01once

    • x402
    • USDC
    • Base
    Get skill

    Tested with Sonnet and Haiku, 30 Sep 2026

  • API Breaking Change Check for OpenAPI

    Code & Engineering

    SKILL.md · v1.0.1 · 10.7 KB

    Compares two versions of one API description (OpenAPI 3.0 or 3.1 in YAML or JSON, or two JSON Schemas of one request or response) and lists ONLY the changes that break clients written against the old version, one line each with a fixed code, the place, old and new, and who breaks - or exactly No breaking changes. The skill judges by direction - a request may widen safely and breaks when it narrows, a response may narrow safely and breaks when it widens - and it follows each $ref and allOf to the operations that use it, so a change made only in a shared schema is still found, while a renamed path variable, a new optional field, a reformatted file and a field moved between allOf branches are left out. Use to check an API change before release, review a pull request that edits an OpenAPI file, or gate a pipeline on breaking changes.

    $0.03once

    • x402
    • USDC
    • Base
    Get skill

    Tested with Sonnet and Haiku, 18 Oct 2026

  • Test Review: Can This Test Ever Fail?

    Code & Engineering

    SKILL.md · v1.0.1 · 10.3 KB

    Reviews a pasted test, check script, smoke test, mutation script or CI step and says whether it can fail when the thing it guards is broken, so a green result is worth something. It finds a completeness guard that counts its own hard-coded list instead of the directory, a one-star glob that never enters subfolders, a mutation script that restores with git checkout and wipes the uncommitted fix under test, a null test that an empty string passes, a status 200 that is only the login page, a wait loop without sleep that measures network speed instead of time, a test that calls the pure function directly and never the call site, a counterfactual control whose data does not create the condition, a fix copied into two branches and tested in one, a limit tested only with ordinary values, and a catch that turns a failure into success. Each finding has a fixed code, the place, why it passes and the one input that would make it fail, or exactly No findings. Use to review a test that cannot fail, a check script that always passes, or a guard before you trust it.

    $0.03once

    • x402
    • USDC
    • Base
    Get skill

    Tested with Sonnet and Haiku, 8 Oct 2026

  • Tests from Spec: Cases from the Requirements

    Code & Engineering

    SKILL.md · v1.0.0 · 6.3 KB

    Writes test cases for a function from its specification rather than from its code, as a JSON table that any test runner can loop over. Every rule, boundary and error the spec defines gets a case, and every expected value is worked out from the spec; where the code you show disagrees with the spec, the case follows the spec and a note says where. Behaviour the spec leaves open becomes a question instead of a guessed case. Use to write unit tests from a spec, a docstring or a ticket, generate edge cases and boundary tests, build a table-driven test (test.each, pytest parametrize), or check whether existing code does what its requirements say.

    $0.03once

    • x402
    • USDC
    • Base
    Get skill

    Tested with Sonnet and Haiku, 8 Oct 2026

More in Code & Engineering

  • API Key Leaks in Code: Secret Handling Review

    Code & Engineering

    SKILL.md · v1.0.0 · 7.8 KB

    Reviews pasted application code, scripts and config for the four ways a secret leaks from the code that uses it, and lists only those, one line each with a fixed code, the place, who can then read the secret and the fix - or exactly No findings. It catches a secret value written to a log or printed (the key itself, an object or environment dump that contains it), a secret placed in a URL query string, a secret typed into source or a committed file, and a server secret sent to the browser through a public environment prefix or a prop of a client component. It leaves out the safe look-alikes: printing only that a key is present, its length or its last four characters, a publishable key that is public by design, and a secret read from the environment on the server. Use to review code that calls an API with a key, to check a Next.js or Vite project before release, or to gate a pull request that touches credentials.

    $0.01once

    • x402
    • USDC
    • Base
    Get skill

    Tested with Sonnet and Haiku, 29 Oct 2026

  • Convert a Cron Expression: Linux, Quartz, GitHub Actions

    Code & Engineering

    SKILL.md · v1.0.0 · 11.4 KB

    Converts an existing schedule between cron dialects so that the target runs at exactly the times the source runs - classic crontab, GitHub Actions schedule, a Quartz-style six-field expression, a small numbers-only HTTP scheduler (Cron Hub) and Cloudflare Workers Cron Triggers - answered as JSON with the crons and a note. Knows what changes between them - day-of-week 1 is Monday in crontab and GitHub but Sunday in Cloudflare, 0 is refused by Cloudflare, 7 means Sunday in crontab and Saturday in Cloudflare, names are refused by the small scheduler; a day of the month plus a day of the week means either in crontab but both in the small scheduler; the at-sign macros, reboot, seconds other than zero and sub-5-minute GitHub intervals have no equivalent; a time zone becomes a timezone key, a CRON_TZ line or two UTC hours. Refuses what cannot be run at the same times. Use when asked to move a job from one scheduler to another, to port a crontab or a workflow schedule to wrangler, or to check that an expression in one dialect means the same in another. For a schedule given in words, use cron-schedule instead.

    $0.02once

    • x402
    • USDC
    • Base
    Get skill

    Tested with Sonnet and Haiku, 9 Oct 2026

  • UI Edge-Case Test Data: Long Names, Empty and Huge Values

    Code & Engineering

    SKILL.md · v1.0.0 · 6.8 KB

    Turns the props of a UI component (a TypeScript type or a list of props) into strict JSON test data that stresses how it displays, and nothing else. Per prop it picks the worst values for the type, a string that is empty, one word of eighty characters with no space, on two lines, an emoji joined with a zero-width joiner, right-to-left letters or HTML-looking text, numbers at zero and at the large and decimal ends, lists that are empty, hold one item or hold a hundred, optional props left out, and dates at the far ends and across a time-zone edge. Every fixture still fits the declared type: a required prop is never missing, an enum gets only its values, a documented range is never broken, and callbacks are named apart from the data. Use to build fixtures for a component story, a visual test or a layout check, or to find what breaks a card, a list row or a badge.

    $0.01once

    • x402
    • USDC
    • Base
    Get skill

    Tested with Sonnet and Haiku, 29 Oct 2026

  • Recommended

    Security Headers Writer: HSTS, CSP, Frames

    Code & Engineering

    SKILL.md · v1.0.4 · 21.4 KB

    Writes the response headers a site is missing, in the format of the person's own host, and keeps the page working. It reads the report line, the current response headers, the file that sets headers and the served head, and gives one line per finding with a fixed code, then a Headers block and a Verify line. It covers a missing or short Strict-Transport-Security, no framing rule, no nosniff, no enforcing Content-Security-Policy (a Report-Only header does not count) and a server version in a header. It enforces only directives that cannot break rendering; script, style, image, font and connect sources go into a Report-Only header built from the origins in the pasted head. It leaves out safe look-alikes: unsafe-inline in style-src only, a long max-age without preload, SAMEORIGIN, a Server header with no number and a missing Referrer-Policy. Exactly No findings. when all is well. Use when a site check reports missing security headers, no HSTS, a short HSTS max-age or a version number in a response header.

    $0.05once

    • x402
    • USDC
    • Base
    Get skill

    Tested with Sonnet and Haiku, 12 Nov 2026