>_devkit
naming-conventions
skills/naming-conventions/

references/code.md

Naming in code

Functions

A function name is a verb phrase describing the effect, precise enough that a reader never has to open the body to know what calling it does.

# vague: what does it do, and does it write anything?
def process_order(order): ...
def handle_bid(bid): ...

# precise
def settle_order(order): ...
def reject_bid_below_reserve(bid): ...

Encode the surprising part. A caller must be able to see side effects, cost, and failure mode from the call site:

NameTells the reader
get_user(id)cheap, in memory, always succeeds or returns None
fetch_user(id)crosses the network, can fail, can be slow
load_user(id)reads from disk or database
ensure_user(id)may create as a side effect
try_parse_date(s)returns a failure rather than raising

Once you pick a verb for a meaning, it means that everywhere. Mixing get and fetch for the same operation forces a lookup at every call site.

Consistent verbs, house set:

get (cheap lookup), fetch (remote), load (storage), list (many), create, update, delete, ensure (idempotent create), build (pure construction), validate (raises or returns errors), is/has/can (predicates), to (conversion), on (event handler).

Argument order and count. More than three positional arguments means the call site is unreadable, and it is usually a missing type:

create_order(user_id, vehicle_id, amount_minor, currency, address_id, notes)   # what is what
create_order(OrderDraft(...))                                                   # one thing

Booleans

Assertions, positively phrased, answerable with yes:

is_active      has_balance      can_refund      should_retry      was_settled

Never store or name a negative. not_ready, disable_cache, hide_footer produce double negatives at the call site, and double negatives are read wrong under time pressure.

if not not_ready:            # wrong every time
if is_ready:                 # right

The exception is a flag whose default must be true: allow_retry=True reads better than forbid_retry=False. Even then, keep it positive.

Variables

Length scales with scope and lifetime.

for i, row in enumerate(rows):        # fine: 3 lines, obvious
    ...

settlement_deadline_utc = ...          # module level: spell it out

No type in the name. user_list, name_str, config_dict restate what the type system already says, and go stale the moment the type changes. users, name, config.

The exception is units, which the type system does not capture and which cause real bugs. Always name them:

price_minor     timeout_ms      size_bytes      distance_km     ttl_seconds

price is ambiguous between 15.00 and 1500. price_minor is not. If your codebase inherits one convention for money, state it once at the top of the data layer and use the suffix everywhere.

No single-letter names outside tight loops or accepted maths (x, y, n, i, j). d for a document, u for a user, r for a response are how a function becomes unreadable at 200 lines.

Avoid the temporary trio: data, info, result, temp, obj, item. Each of them means "I did not want to think of a name". payload, bid_response, settled_orders cost nothing and save a read.

Classes and types

Noun phrases for what it is, or what it does if it is a service:

Auction            AuctionSettler         BidValidator        PriceCalculator

Not AuctionManager, BidHelper, PriceUtils. Those names appear when a class has no single responsibility, so the name is a design signal: if you cannot name it without Manager, split it.

No prefixes. No IUserRepository in TypeScript, no CUser, no _private class names. The language already knows.

Suffixes that carry meaning are fine, when they are a real pattern and used consistently: ...Repository, ...Error, ...Config, ...Client, ...Job.

Errors end in Error and say what failed:

class InsufficientDepositError(DomainError): ...
class AuctionAlreadySettledError(DomainError): ...

Not AuctionError for nine different failures, since the caller then has to inspect a message string to decide what to do.

Files and modules

Python: snake_case.py, named for the domain, singular or plural to match the contents.

bank_accounts.py        auctions.py        settlement.py

TypeScript, non-component: kebab-case.ts.

skill-builder.ts        install-script.ts       use-paged-list.ts

React components: PascalCase.tsx, matching the exported component exactly. One component per file for anything exported.

ResourceTable.tsx  ->  export function ResourceTable()

This is the one place where file casing differs, and it is worth it: the import statement and the JSX tag then read identically, and a mismatch between file name and export is immediately visible.

Do not repeat the folder in the file. auctions/auctions_service.py reads as auctions.auctions_service. Use auctions/service.py.

Test files mirror the module under test: auctions.py gets test_auctions.py. When a test file grows past a few hundred lines, split by behaviour and say so in the name: test_auctions_settlement.py.

Constants and enums

Module constants are SCREAMING_SNAKE, and only when genuinely constant.

MAX_BID_MINOR = 10_000_000
DEFAULT_PAGE_SIZE = 50

Enum members are the domain word, lowercase in the wire format:

class OrderStatus(str, Enum):
    draft = "draft"
    paid = "paid"
    settled = "settled"

Short strings, not integers. status: "paid" is greppable in logs and readable in a database shell; status: 3 requires a lookup table nobody has.

Smells

Names that reliably indicate a design problem, not just a style problem:

NameWhat it usually means
XManager, XHelper, XUtilsNo single responsibility. Split it.
data, info, payload2The concept was never named.
process(), handle(), run()Does several things. Name each.
flag, mode, type as a parameterA boolean parameter that should be two functions.
do_x_and_y()The and is telling you it is two functions.
utils.py growing past a few functionsA missing module, waiting to be named.
temp_fix, new_, v2_ in a nameVersion in the name. The old one should have been deleted.

new_ and v2_ deserve emphasis: they are permanent. Two years later process_order_v2 is the only one in use, process_order is dead, and nobody dares delete either.