Find opportunities for code reuse in the changed code, both against existing
code and within the change itself, without creating bad coupling or contrived
abstractions.
Target
jj show --git
Arguments: $ARGUMENTS
Find reuse opportunities in the target named in the arguments if given.
Otherwise find them in the changes in the current commit shown above. If there
are no arguments and the commit has no changes, ask the user which code to
review and stop.
Principles
- Duplication is only a problem when the copies must change together. Two pieces
of code that look alike but encode different decisions are coincidentally
similar. Merging them couples unrelated change reasons
- Prefer reuse that adds no new code: call something that already exists in the
codebase, the standard library, or an installed dependency
- A little duplication is cheaper than the wrong abstraction. When in doubt,
leave the copies
Workflow
- For each changed function or code chunk, search for code that already does
the job, in order of preference:
- The standard library of the language
- Dependencies already installed, listed in the manifest, e.g.
package.json. Don't propose new dependencies
- Utilities and helpers elsewhere in the codebase
- Other code in the same module
- Compare the added chunks against each other: a change often introduces the
same logic twice in different files, and neither copy existed before, so
searching existing code won't find it
- For each duplication found in steps 1 and 2, decide whether to reuse or
extract, using the tests below
- If nothing passes the tests, say so and stop. Don't invent findings
- Report each opportunity (see "Reporting"). Don't apply changes unless the
arguments or a follow-up message ask for it
Tests for a reuse opportunity
Flag an opportunity only when it passes all of these:
- Same reason to change: if a requirement shifts, every call site must require
the new behavior. If one caller might need to diverge, it's coincidental
similarity
- Precise name: the shared code has a name that describes one job. If the best
available name is vague (
helper, util, process, handleData), the
abstraction is contrived
- No parameter switches: the shared code needs no boolean flags, mode enums, or
callbacks whose only purpose is to make callers behave differently. Each such
parameter reintroduces the duplication
- Dependencies point downward: callers depend on something at a lower level of
abstraction. Never make a general module import from a specific feature, and
never couple two unrelated features to share a few lines
- Worth the cost: the shared code saves more than it costs. Extracting three
trivial lines into a new shared module fails this; replacing a hand-rolled
deep-clone with an existing utility passes
These aren't exhaustive. Reason from first principles when none fits cleanly.
Reporting
For each opportunity, one paragraph: the duplicated or reimplemented logic (file
path and line range), the call sites it covers, what to reuse or extract
instead, and the test that came closest to failing, with why it still passes.
Order by impact, largest first.
1---2name: reuse3description: |4 Find opportunities for code reuse in the changed code, both against existing5 code and within the change itself, without creating bad coupling or contrived6 abstractions.7argument-hint: '[extra guidance or files to focus on]'8---910Find opportunities for code reuse in the changed code, both against existing11code and within the change itself, without creating bad coupling or contrived12abstractions.1314# Target1516```!17jj show --git18```1920Arguments: $ARGUMENTS2122Find reuse opportunities in the target named in the arguments if given.23Otherwise find them in the changes in the current commit shown above. If there24are no arguments and the commit has no changes, ask the user which code to25review and stop.2627# Principles2829- Duplication is only a problem when the copies must change together. Two pieces30 of code that look alike but encode different decisions are coincidentally31 similar. Merging them couples unrelated change reasons32- Prefer reuse that adds no new code: call something that already exists in the33 codebase, the standard library, or an installed dependency34- A little duplication is cheaper than the wrong abstraction. When in doubt,35 leave the copies3637# Workflow38391. For each changed function or code chunk, search for code that already does40 the job, in order of preference:41 1. The standard library of the language42 2. Dependencies already installed, listed in the manifest, e.g.43 `package.json`. Don't propose new dependencies44 3. Utilities and helpers elsewhere in the codebase45 4. Other code in the same module462. Compare the added chunks against each other: a change often introduces the47 same logic twice in different files, and neither copy existed before, so48 searching existing code won't find it493. For each duplication found in steps 1 and 2, decide whether to reuse or50 extract, using the tests below514. If nothing passes the tests, say so and stop. Don't invent findings525. Report each opportunity (see "Reporting"). Don't apply changes unless the53 arguments or a follow-up message ask for it5455# Tests for a reuse opportunity5657Flag an opportunity only when it passes all of these:58591. Same reason to change: if a requirement shifts, every call site must require60 the new behavior. If one caller might need to diverge, it's coincidental61 similarity622. Precise name: the shared code has a name that describes one job. If the best63 available name is vague (`helper`, `util`, `process`, `handleData`), the64 abstraction is contrived653. No parameter switches: the shared code needs no boolean flags, mode enums, or66 callbacks whose only purpose is to make callers behave differently. Each such67 parameter reintroduces the duplication684. Dependencies point downward: callers depend on something at a lower level of69 abstraction. Never make a general module import from a specific feature, and70 never couple two unrelated features to share a few lines715. Worth the cost: the shared code saves more than it costs. Extracting three72 trivial lines into a new shared module fails this; replacing a hand-rolled73 deep-clone with an existing utility passes7475These aren't exhaustive. Reason from first principles when none fits cleanly.7677# Reporting7879For each opportunity, one paragraph: the duplicated or reimplemented logic (file80path and line range), the call sites it covers, what to reuse or extract81instead, and the test that came closest to failing, with why it still passes.82Order by impact, largest first.