# Grep The Types Statics First

> Before writing any logic over a type the project owns — merging, defaulting, unwrapping, normalizing, converting — grep that type's own static/factory methods and search the codebase for the verb you need. A mature codebase has already solved it, with the edge cases handled and a name the reviewer reads fluently. A plausible hand-rolled helper is worse than a bug: it compiles, it passes, and it reads to a maintainer as unfamiliarity with the house. When you do find the helper, check its RETURN TYPE against your call site before swapping, and verify by running rather than reading. Use before writing any utility loop over a house type. Trigger terms: helper method, merge params, apply defaults, unwrap, convert, write a small utility, is there an existing, reinvent, "I'll just loop over".

- Skill: `serhiy-bzhezytskyy/grep-the-types-statics-first` (Agent Skill)
- Install (CLI): `npx skillmds@latest add serhiy-bzhezytskyy/grep-the-types-statics-first`
- Raw SKILL.md: https://api.skillmd.com/api/skills/serhiy-bzhezytskyy/grep-the-types-statics-first/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: serhiy-bzhezytskyy (https://skillmd.com/u/serhiy-bzhezytskyy)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/serhiy-bzhezytskyy/grep-the-types-statics-first

---


# Grep the type's statics first

## Purpose

Given "apply these defaults without clobbering what the caller set", the obvious move
is a loop: iterate the names, check whether the target already has each one, set it if
not. Eight lines, correct, passes.

And in a codebase that has existed for fifteen years, that operation has a name, a
one-line call, and edge-case handling you didn't write. The hand-rolled version costs
three ways: it is longer, it duplicates semantics that can now drift apart, and — the
expensive one — it signals to the reviewer that you didn't look. Reviewers read
unfamiliarity with their own API as a proxy for care, and increasingly as a tooling
signature.

The check is one grep. It is cheaper than the code it replaces.

## When to use

- Before writing any loop or helper that manipulates a type the project owns.
- Especially for these verbs: merge, default, override, unwrap, flatten, normalize,
  convert, copy-with-change, wrap.
- When a reviewer asks "why not use X?" — treat it as a class of miss, not one miss,
  and sweep the rest of the diff for siblings.

## When NOT to use

Don't stall on it. Two greps and a look at the type's public surface is the whole
budget; if nothing turns up, write the loop and say in the PR that you looked.

## The practice (checklist)

- [ ] Read the type's own factory/utility surface:
      `grep -n "public static" path/to/TheType.java`
- [ ] Search the codebase for the verb, in the house's vocabulary:
      `grep -rn "wrapDefaults\|applyDefaults\|withDefaults\|merge" --include='*.java' <module>/src/java | head`
- [ ] Ask **"what would this be called here?"** Names cluster by house —
      `wrapDefaults`, `wrapAppended`, `of`, `getRootCause`, `copyOf`.
- [ ] Prefer the house call **even when yours works**: fewer lines, inherits the edge
      cases, and reads fluently to the reviewer.
- [ ] ⚠️ Check the **return type** before swapping. A lazy view is not a mutable
      builder; if the call site mutates downstream, you may need to materialize.
      *Done when* you have identified what the call site does with the value.
- [ ] Verify by **running**, not reading — a signature match is not a semantics match.
- [ ] When one is found, sweep for siblings: the same miss usually occurs more than
      once in a diff.

## Rationalizations

| Shortcut | Why it fails |
|---|---|
| "My loop is correct and tested." | Correctness was never the question. It is longer, duplicates semantics that can drift, and reads as not having looked. |
| "I don't know what it would be called." | That is what the two greps are for. `public static` on the type is a 30-line list. |
| "The helper returns the wrong type." | Sometimes true — then wrap or materialize, and say why in the PR. Check before concluding it. |
| "I'll refactor it later." | Later is after review, and review is the scarce resource you just spent. |
| "The reviewer only flagged one place." | They flagged one instance of a class. Sweep the diff. |

## RECEIPT

**A Solr committer, apache/solr PR #4640, 2026-08** — review comment, verbatim:

> "I can see you (really the LLMs you use :-) ) are unfamiliar with
> `SolrParams.wrapDefaults` use that.  Even has the null check."

The hand-rolled version was an 8-line loop merging parser params into
`ModifiableSolrParams`, skipping any the request had already set. `wrapDefaults` had done
exactly that for years, and does carry the null check
(`if (defaults == null) return params;`). The method went from 15 lines to 7.

**The sibling miss, same diff:** `new MapSolrParams(Map.of(k, v))` had a one-line house
form, `SolrParams.of(k, v)`. One `grep "public static"` on `SolrParams` would have found
both.

**The return-type trap, same call site:** `wrapDefaults` returns a lazy view, but
downstream code calls `.remove()` on the result, so it must stay a `ModifiableSolrParams`.
The swap was right — wrap, then materialize — but the naive substitution would not compile.

**Counter-receipt, so the check has a stop condition:** in the same file
`new NamedList<>(Map)` looked like the helper for a `Map`→`NamedList` conversion. Reading
it showed it copies values verbatim (`nvPairs.add(ent.getValue())`) with no recursion, so
it could not serve a recursive normalizer. Hand-rolling was correct there — and
*"I checked, it doesn't recurse"* is a far stronger PR sentence than silence.

## Lifecycle

- **Signals it worked:** no reviewer asks "why not use X?"; diffs shrink because the
  house call replaces a loop.
- **What to log on a misfire:** the type, the helper you missed, and the verb you were
  searching for — the verb vocabulary per house is the reusable part. Record it in
  [`LEDGER.md`](../../LEDGER.md).
- **Death criterion:** none foreseeable; every mature codebase has this surface.
- **Relates to:** sibling to match-the-house-shape (that one sizes the PR to the house,
  this one writes the code in the house's vocabulary) and to
  obey-the-houses-own-tooling — all three are "the house already solved this".

