write-discoverable-code
Rules for writing code that coding agents (and humans) can find and understand through plain-text search.
Skill by Modem
Chalk Labs
Need help deploying write-discoverable-code?
Chalk Labs sets up agents like this inside real teams: connected to your tools, tested on your data, with a human in the loop where it matters.
Install
Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.
npx agentmarket install skill/michalvavra-write-discoverable-code-7c7c8d
Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Use cases: documents, backend, writing
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
Permissions: not checked yet.
What's inside
- Skill name
write-discoverable-code- Description
- Rules for writing code that coding agents (and humans) can find and understand through plain-text search. Apply whenever writing or renaming code: functions, types, constants, files, error messages, doc comments. Grounded in measurement: agents navigate by plain-text search, not by AST or language server, so every identifier is a search query and every search miss costs wasted reads.
- License
- MIT
- Metadata
- author: Modem · source: https://github.com/modem-dev/skills/tree/edcdedb38a545f67c065f4084b3627517f0d79cf/write-discoverable-code
- Instructions
- 108 lines in SKILL.md · view on GitHub
Files (2)
- LICENSE
- SKILL.md
SKILL.md (first lines)
# Write discoverable code
Coding agents discover code by searching for strings and reading small windows around the
hits. They have no hover text, no jump-to-definition, and no memory between sessions. These
rules make code resolvable in one search instead of five.
## 1. Names are search queries
- **Exported symbols get 2–4 word names, at least one of them a domain word.**
`diffUserObjects`, not `diff`. `queueEventForDispatch`, not `queue`.
Measured on a ~700k-line monorepo: 1-word exported names are globally unique 61% of
the time; 3-word names 96%; 4+ words 98%. Three words is the knee of the curve.
Use the shortest name that greps uniquely; put the rest in the doc comment.
- **Give generic verbs their object.** `sanitizeEmailHtml`, not `sanitize`;
`validateSmtpConfig`, not `validateConfig`. Qualify only as far as uniqueness
requires, then stop.
- **One definition site per symbol.** Never copy a function between files; move it and
delete the original in the same change. Shared helpers get one concept-named home
and are imported everywhere else.
- **Do not rely on the module path to disambiguate a generic name.** The import that
disambiguates `users/diff.ts` from `orders/diff.ts` sits at the top of the file; the
search hit is at line 300. Put the context in the symbol (`formatDurationMs`), not the
folder. Exception: rigid, absolute conventions where the path carries the meaning
(e.g. every contract file exporting `Input`/`Output`). Folders still group related
files (section 2); they just don't carry the name's context.
- **One concept, one spelling.** Pick `organizationId` or `orgId` and use it everywhere;
every synonym splits every future search in half. Reuse existing vocabulary in the
codebase you are editing rather than introducing near-synonyms.
- **When behavior or audience changes, rename in the same commit.** A stale name is
misinformation with a 100% open rate — that includes visibility markers: a `_private`
helper that other modules now import needs a public name.
- **Filenames are names too — never use bare-role filenames.** `config.ts`, `types.ts`,
`utils.ts`, `helpers.ts`, `handlers.ts` say nothing in a search result and collide with
every other module's config/types/utils in the repo. Prefix the domain:
`billing-plan-config.ts`, not `config.ts`. (`index.ts` is acceptable only as a
thin re-export entry point.)
## 2. Folders are the map
A directory listing is the first search in an unfamiliar codebase, and it should read
as a map of the system. Group related files into folders — by feature, domain, or
layer, whichever organizing principle the codebase already follows — so each listing
shows a handful of named areas rather than every file at once. Folders carry the
grouping; context still lives in the symbol (section 1), so a name should make sense
even when quoted without its path.
## 3. Types are the documentation agents can't skip
- **Brand your primitive IDs.** `z.string().brand<'UserId'>()` (TS) or newtypes (Rust).
A `transferOwnership(userId: string, orgId: string)` signature makes argument
transposition invisible; branded types make it a compile error that names the concepts.
- **Use capability-token parameter types** for privileged operations (e.g. requiring an
`OrgScopedDb` instead of a raw connection). A comment is a request; a required type is
physics.
- **Model state with discriminated unions**, not clusters of nullable fields with implicit
rules.
- **Name types like they'll be quoted back** — they will be, in compiler errors the agent
uses to self-correct. `OrgScopedDb` explains itself; `Ctx2` does not. Avoid `any`: every
`any` is a spot where the compiler goes silent and the agent is back to guessing.
## 4. Say it where the search lands
- **One-line doc comment on every export**, stating the sharpest constraint the code
itself can't show (units, timezone, "source time, not insert time", ownership).
The definition is where a name search lands; that line is your whole message.
- **Write the plain-words phrase in the doc comment.** Searches arrive as natural language
("rate limit", "retry delay"), and camelCase identifiers don't match phrase greps —
`RateLimiter` is invisible to a search for "rate limit". The doc comment above each
export should contain, in ordinary spaced-out words, the phrase someone would search
for: a `SessionExpiryChecker` should say /\*_ Checks whether the user session has
expired. _/ so that a grep for "session expired" or "session has expired" lands here.
- **A module should make sense with its imports unread.** Each imported name plus its
doc line should say enough that the reader never has to open the source module. If
they do, the import's name is failing, not the reader.
- **Keep strings whole.** Never build event names, flags, or error codes with template
interpolation (`` `github.${entity}.${action}` `` makes `github.pr.merged` unsearchable).
Write the full literal even when a loop feels DRYer.
- **Error messages start with a unique literal prefix**, so a message seen in a log greps
straight back to the throw site. ``throw new Error(`Webhook signature mismatch for ${id}`)``,
never ``throw new Error(`${prefix}: mismatch`)``.
- **One searchable concept per file, and keep orchestrators thin.** The code that answers
"where is X done?" should live in a module named after X — the thing a reader would
ask about, not the mechanism inside — not inline in a coordinator,
pipeline, or service class. An orchestrator should read as a sequence of calls into
well-named modules; if a reader lands in it from a search, every line should point them
one hop from the real implementation. Burying the implementation of several concepts in
one large file makes every search for any of them land on the same wall of code.
Split until each question-sized concept has one named home, then stop: a helper
meaningful only inside one concept belongs inline, and a file per tiny function
fragments one answer across several reads. The test runs both ways: a module that
answers many unrelated questions is holding more than one concept.
- **Colocate tests** (`foo.test.ts` next to `foo.ts`) so one search finds behavior and its
specification together.
- **Mark dead ends.** `@deprecated` on the old path, with a pointer to the new one.
## Quick checklist before committing
1. Would one search for each new exported name be enough to find its implementation?
2. Would swapping two arguments of the new function fail the build?
3. Is the one thing a caller must know but the signature can't say (units, timezone,
ownership, ordering) written right at the definition?
4. Do all log/error strings exist verbatim in the source?
5. Did anything change behavior without changing its name?
6. When code moved, is it gone from where it came from?
7. Does each new file sit in a folder alongside related code, and does the listing
still read as a map of the system?From michalvavra/agents, MIT.
More from michalvavra/agents
- 0.645score
good-css
Vojta Holík
Modern CSS techniques that replace breakpoint ladders, wrapper elements and scripts.
SkillAFreshness: A★ 16Claude Code +9 - 0.640score
version-releases
michalvavra
Use when preparing, reviewing, or documenting versioned app releases, changelogs, SemVer bumps, release commits, git tags, or tag-gated production deploys
SkillAFreshness: A★ 16Claude Code +9 - 0.640score
html-artifacts
michalvavra
Build self-contained single-file HTML artifacts (data reports, code reviews, technical plans, architecture plans and other docs) with a shared, quiet…
SkillAFreshness: A★ 16Claude Code +9 - 0.626score
writing-pr
Luke Parker
Use when writing or editing a pull request title or body.
SkillAFreshness: A★ 16Claude Code +9 - 0.615score
diagnosing-bugs
Matt Pocock
Diagnosis loop for hard bugs and performance regressions.
SkillAFreshness: A★ 16Claude Code +9 - 0.610score
grilling
Matt Pocock
Grill the user relentlessly about a plan, decision, or idea.
SkillAFreshness: A★ 16Claude Code +9 - 0.609score
writing-for-agents
Matt Pocock
Writing documents for agents.
SkillAFreshness: A★ 16Claude Code +9 - 0.599score
handoff
Matt Pocock
Compact the current conversation into a handoff document for another agent to pick up.
SkillAFreshness: A★ 16Claude Code +9 - 0.596score
write-discoverable-code
michalvavra
Make changed code easy for agents and humans to find, understand, and verify through search-oriented naming, precise types, truthful local guidance, coherent…
SkillBFreshness: B★ 16Claude Code +9 - 0.592score
architecture-review
michalvavra
Staff-level codebase health review.
SkillBFreshness: B★ 16Claude Code +9 - 0.545score
commit
michalvavra
Create git commits using Conventional Commits format.
SkillCFreshness: C★ 16Claude Code +9 - 0.485score
gogcli
michalvavra
Google Workspace automation via gogcli for Gmail, Calendar, Drive, Contacts, Tasks, and Sheets.
SkillDFreshness: D★ 16Claude Code +9
Signals
- Stars
- 16
- Forks
- 1
- Open issues
- 0
- Last commit (this folder)
- Oct 23, 2026 (7 days ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- MIT
- Repository
- github.com/michalvavra/agents
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
Same skill elsewhere
Copies of this SKILL.md published by others; search shows only one of them.
Done for you
Rather not set it up yourself?
Chalk Labs scopes, builds and runs agents for your team: the task audit, the eval harness, the integrations and the weekly ops report. Fixed scope, priced before we start.
See how we build AI agents →Read the case studies
Chalk Labs is a two-founder AI and growth studio. We build AI agents, CRMs and MVPs for Web3 and AI companies, and have shipped 50+ product and token launches.
- launches shipped
- 50+
- launches shipped
- media placements
- 500+
- media placements
- founders on every account
- 2
- founders on every account
Press we've placed: Forbes · CoinDesk · Decrypt · Cointelegraph · The Block
Rahil Jain, Co-founder, Chalk Labs. Ex-Goldman Sachs engineer. You talk to the person who builds it.
Get write-discoverable-code running in your business
Tell us what you want to automate. A founder replies within one working day with a plan, not a pitch deck.
Cite this page
write-discoverable-code skill. Agent Mart. https://agentmart.dev/skills/michalvavra-write-discoverable-code-7c7c8d (last updated 2026-10-24)