Why naming matters more than you think
A function called handle() says nothing. What does it handle? Errors? Clicks? Data? The name is inline documentation. If you need to read the function body to understand what it does, the name failed. processPayment() is clear: it processes a payment. doStuff() is cognitive garbage.
Verbs matter. Queries (reads) use get, find, fetch, query. Mutations (writes) use create, update, delete, add, remove. Validations return boolean: isValid, hasPermission, canEdit. This consistency isn't whimsy, it's contract communication.
Domain context defines the verb. In e-commerce, placeOrder() is more precise than createOrder() because it communicates the user action. In inventory systems, reserveStock() says more than updateInventory(). Naming reveals the business mental model. Readable code is code that speaks the language of the problem it solves.
Conventions your team should respect
Boolean functions start with is, has, can, should. isActive(), hasPermission(), canEdit(). Never checkActive() or verifyPermission() that return boolean—those verbs imply side effects. The convention prevents surprises: if it starts with is, you know it returns pure boolean without modifying state.
Prefixes get vs fetch: get is synchronous, direct access (e.g. getProperty()). fetch implies asynchronous operation, network or I/O (e.g. fetchUserData()). This distinction helps anticipate if the function is expensive or instant. getUserName() returns string from object; fetchUserName() makes HTTP request.
Avoid redundant suffixes: getUserData() is redundant if the function already says it returns data. Better getUser(). Same logic with sendEmailNotification()—sendEmail() suffices if context is clear. Fewer words = more readability, as long as you don't sacrifice clarity. The balance is tricky but achieved through iterating in code reviews.
Mistakes that reveal junior code
Generic functions like handleData(), processInfo(), manageStuff(). They're red flags. If you can't give it a specific name, the function probably does too many things. Refactor into smaller functions with clear responsibility. handleData() should become validateUserInput() + saveToDatabase() + sendConfirmationEmail(). Three clear names, three unit functions.
Cryptic abbreviations: updUsrPrf(). This isn't 1980s assembly code. Characters don't cost money. updateUserProfile() is perfectly readable. The only exception: universal domain abbreviations (HTML, URL, ID). But usr, prf, mgr are laziness that impacts maintainability.
Inconsistency: in one file you use getUserById(), in another findUserById(), in another retrieveUserById(). Three different verbs for the same action. Pick one and use it across the codebase. Tools like ESLint with custom rules can enforce conventions. Naming should be boring and predictable, not creative. Save creativity for architecture, not synonyms for 'get'.
Naming in TypeScript-specific contexts
Factory functions return new objects: createUser(), buildQuery(), makeRequest(). Never mutate arguments. If you mutate, the name must reflect it: addItemToCart(cart, item) suggests mutation; withItemAdded(cart, item) suggests immutability (returns new cart). Naming communicates whether your function is pure or has side effects.
Event handlers: handleClick(), onSubmit(), processInput(). The handle or on prefix indicates it's a callback. React hooks callbacks use handle: handleChange, handleSubmit. Props that receive callbacks use on: onClick, onValueChange. Consistency between components avoids confusion.
Async functions should always suggest asynchrony in the name if not obvious from context: fetchData() (obvious), loadConfig() (obvious). But getUser() sync vs getUser() async causes bugs. If the async version exists alongside sync, name them differently: getUserSync() and fetchUser(), or getUser() sync and loadUser() async. The compiler won't save you from returning Promise<User> when you expected User if the names are identical.