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: 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.4---56Find opportunities for code reuse in the changed code, both against existing7code and within the change itself, without creating bad coupling or contrived8abstractions.910# Target1112```!13jj show --git14```1516Arguments: $ARGUMENTS1718Find reuse opportunities in the target named in the arguments if given.19Otherwise find them in the changes in the current commit shown above. If there20are no arguments and the commit has no changes, ask the user which code to21review and stop.2223# Principles2425- Duplication is only a problem when the copies must change together. Two pieces26 of code that look alike but encode different decisions are coincidentally27 similar. Merging them couples unrelated change reasons28- Prefer reuse that adds no new code: call something that already exists in the29 codebase, the standard library, or an installed dependency30- A little duplication is cheaper than the wrong abstraction. When in doubt,31 leave the copies3233# Workflow34351. For each changed function or code chunk, search for code that already does36 the job, in order of preference:37 1. The standard library of the language38 2. Dependencies already installed, listed in the manifest, e.g.39 `package.json`. Don't propose new dependencies40 3. Utilities and helpers elsewhere in the codebase41 4. Other code in the same module422. Compare the added chunks against each other: a change often introduces the43 same logic twice in different files, and neither copy existed before, so44 searching existing code won't find it453. For each duplication found in steps 1 and 2, decide whether to reuse or46 extract, using the tests below474. If nothing passes the tests, say so and stop. Don't invent findings485. Report each opportunity (see "Reporting"). Don't apply changes unless the49 arguments or a follow-up message ask for it5051# Tests for a reuse opportunity5253Flag an opportunity only when it passes all of these:54551. Same reason to change: if a requirement shifts, every call site must require56 the new behavior. If one caller might need to diverge, it's coincidental57 similarity582. Precise name: the shared code has a name that describes one job. If the best59 available name is vague (`helper`, `util`, `process`, `handleData`), the60 abstraction is contrived613. No parameter switches: the shared code needs no boolean flags, mode enums, or62 callbacks whose only purpose is to make callers behave differently. Each such63 parameter reintroduces the duplication644. Dependencies point downward: callers depend on something at a lower level of65 abstraction. Never make a general module import from a specific feature, and66 never couple two unrelated features to share a few lines675. Worth the cost: the shared code saves more than it costs. Extracting three68 trivial lines into a new shared module fails this; replacing a hand-rolled69 deep-clone with an existing utility passes7071These aren't exhaustive. Reason from first principles when none fits cleanly.7273# Reporting7475For each opportunity, one paragraph: the duplicated or reimplemented logic (file76path and line range), the call sites it covers, what to reuse or extract77instead, and the test that came closest to failing, with why it still passes.78Order by impact, largest first.