🎁 Get the FREE AI Skills Starter Guide β€” Subscribe β†’
BytesAgainBytesAgain
πŸ¦€ ClawHub

Duplication Removal Via Extraction

by @quochungto

Remove duplicated code across methods and classes by extracting small shared utilities first and letting larger structure (superclass, Template Method) emerg...

Versionv1.0.0
⚑ When to Use
TriggerAction
The instinct is often to plan a grand restructuring before touching anything. Resist it. Duplication removal does not require a reengineering effort. You can remove it in small, safe steps while doing your regular work, and the larger structure will reveal itself.
Before arriving here, run `characterization-test-writing` to establish a regression safety net across all the duplicated sites. Every step in this process makes a mechanical change. Without tests, you cannot distinguish "still correct" from "silently broken."
Do not use this skill when the similarity is coincidental β€” two classes that happen to use the same 3-line sequence but for unrelated reasons. Coupling them creates a false dependency. The test is: if one copy needed to change, would the other need to change too? If yes: real duplication. If no: coincidental similarity.
πŸ’‘ Examples

Example 1: LoginCommand + AddEmployeeCmd β†’ Command Superclass

Situation: Two Java command classes both contain the repeated pair outputStream.write(field.getBytes()); outputStream.write(0x00); for every string field they write. Both classes write header, command char, fields, footer β€” same structure, different fields.

Step 2 β€” Smallest fragment: The two-line write-and-terminate pair. Name: writeField(OutputStream, String).

Step 3 β€” Extract locally in LoginCommand:

private void writeField(OutputStream outputStream, String field) throws Exception {
    outputStream.write(field.getBytes());
    outputStream.write(0x00);
}
Replace all write/0x00 pairs in LoginCommand.write() with writeField(...) calls. Tests pass.

Step 4 β€” Replicate in AddEmployeeCmd: Same method, identical signature. Replace its pairs. Tests pass.

Step 5 β€” Introduce superclass: Create abstract Command class. Both classes extend it. Tests pass.

Step 6 β€” Pull up: Move writeField to Command. Delete local copies. Tests pass.

Step 8 β€” Emergence: After writeField is shared, both write() methods are structurally identical: header β†’ size β†’ commandChar β†’ writeBody() β†’ footer. Extract writeBody() as abstract. Pull write() to Command. Pull shared constants (header, footer, SIZE_LENGTH). The subclasses reduce to constructor + writeBody() + getCommandChar().

Final state: Command holds all protocol logic. Adding a new command is one subclass, no copy-paste.

Example 2: Two Validators with Repeated Null-Check Pattern

Situation: UserValidator and OrderValidator both open with:

if (input == null) {
    throw new IllegalArgumentException("input must not be null");
}
The guard is identical across both classes and three other validators as well.

Step 2 β€” Smallest fragment: The three-line null guard. Candidate name: requireNonNull(Object, String).

Steps 3–4: Extract to local method in UserValidator, replicate in OrderValidator. Tests pass in both.

Step 5: Check β€” do the validators share a parent? If they all extend a BaseValidator, pull up there. If not, decide: introduce AbstractValidator or extract to a shared utility class (ValidationUtils.requireNonNull). Choose based on whether validators truly share an identity or are only incidentally similar.

Step 8 β€” Observe: After the guard is shared, look at whether the remaining validation logic also shares structure. If all validators do: guard β†’ validate fields β†’ aggregate errors β†’ return result, Template Method is emerging. Extract validateFields() as abstract and pull the outer structure up.

Example 3: Coincidental Similarity β€” Do NOT Extract

Situation: FilePermissionChecker and DatabaseConnectionPool each contain:

if (count < 0) {
    throw new IllegalArgumentException("count must be non-negative");
}

Analysis: FilePermissionChecker checks the number of retries. DatabaseConnectionPool checks the pool minimum size. These are semantically unrelated. The two classes have nothing in common β€” no shared parent makes sense, no shared utility class is appropriate.

Decision: do not extract. The similarity is coincidental. If the retry validation rule changes (e.g., to allow 0 retries), you would change FilePermissionChecker but not DatabaseConnectionPool. Coupling them would create false dependencies and make both harder to change.

The test: "If one copy needed to change, would the other need to change too?" Answer here: no. Leave them independent.

View on ClawHub
TERMINAL
clawhub install bookforge-duplication-removal-via-extraction

πŸ§ͺ Use this skill with your agent

Most visitors already have an agent. Pick your environment, install or copy the workflow, then run the smoke-test prompt above.

πŸ” Can't find the right skill?

Search 60,000+ AI agent skills β€” free, no login needed.

Search Skills β†’