Consistency in naming makes reading and memory retrieval much, much easier. Conversely, changing rules and mixing conventions are very confusing and significantly increase cognitive load. Follow language, company, and project conventions for names, even if you don't like them.
Most programming languages have stylistic conventions for writing names - if not official, most communities provide their own. Use language-specific code linters to check the code.
Only use words and abbreviations that are in the dictionary. Use abbreviations and acronyms only if everyone knows what they mean. Make exceptions for abbreviations such as id
and other well-known or documented domain-specific abbreviations. Names can become confusing if spelling errors are made, especially inconsistently. In general, abbreviations, if not well-known, add further ambiguity.
-
Avoid neologisms and slang.
Use existing, well-known names, especially if your team is not all native English speakers.
-
Avoid letter-only names.
Especially single-letter names. Yes, this counts for loops too. Unless you're using C, there is little reason for using manual loops anyway, as most languages support functional iteration.
for (Element element : elements) { ... } for (int i = 0; i < elements.size; i++) { val element = elements[i]; ... }
-
Avoid esoteric abbreviations and acronyms.
Especially arbitrary ones you too will forger in five minutes. Example:
while(i_love_lucy_is_on_tv) { xqtc_fn(); }
This will make it easier for your brain to remember them.
-
Avoid special symbols.
Make exception for
_
in snake case and other special conventions. For example,>=>
and<*>
are valid function identifiers in Scala, colloquially named fish and space ship. In Clojure,->
function is a well-known "thread-first" macro (together with=>
, "thread-last" macro"). -
Only use underscores between words (and other symbols).
Example violation:
_ans_var_question_05
ortrivWndFeedback_
. -
Only use one underscore at a time.
Exception: In Python, double underscore indicates special method defined by the Python language, e.g.
__init__
.
No hard rules here; use your good judgement. In general:
-
Prefer full-words over abbreviations.
-
Limit name word count (<= 4). Avoid unnecessary context and keep your name length to a maximum of four words. Names should be limited to the maximum number of words people can read at a glance.
-
Limit character length. Keep name length within 20 characters.
-
Non-reducible longer length name marks a problem in technical design in most cases. For example:
isCurrentSquareNextToAtLeastOneBlockedSquare(currentSquare) // could be moved into a method (if using OOP): currentSquare.nextToBlockedSquare()
-
Make names differ phonetically.
Example:
data_earth
anddate_of_birth
. -
Make names differ by more than word order.
For example,
employeeCount
andcountEmployees
in the same lexical context would be very easy to mix up. -
Don't shadow existing names.
-
Avoid numeric suffixes. Do not add numbers to multiple identifiers that share the same base name, e.g:
val employee1 = convert(employee) val employee2 = convertSomeMore(employee1)
Don't rewrite a value as a name; name what the value represents. Replace number names with either domain-specific terms, such as PI
, or a name that describes the concept that the number represents, such as BOILING_POINT
. However, don't define constants for values which don't have names and/or are not special:
#define ONE_THIRD 0.25
Make sure that a name is not:
- too specific
- overspecifying/leaking implementation
- too long or consisting of too many words to read at a glance
- too ambiguous
- expressing a too generic concept
- being a homonym (having multiple meanings)
- an abbreviation that is unobvious to a junior
- incorrect, i.e. expressing the wrong concept
- inconsistent, i.e. expressing an already-named concept using a different name.
-
Use large vocabulary to avoid homonyms and synonyms.
file
can be replaced withfilename
,filepath
,fileContents
orfileHandle
, according to it's specific meaning. If you're trying to express the concept of "perimeter", use identifierperimeter
instead ofboundary.
-
At the same time, make sure you're consistent in your naming and not using too exoteric words, especially if the team is not speaking English natively.
-
Use problem domain terms.
Prefer terms which relate to the problem the software is solving or that the client is using.
-
Be specific.
Avoid abstract and generic words like it, everything, data, handle, stuff, do, routine, perform. Examples:
func_1() handle_data() do_args_method()
-
Abbreviate with care.
As a rule of thumb, if you're not sure if a junior in your team knows particular abbreviation, it's a good idea to skip it. Also, watch whether the abbreviation is ambigous. Examples:
e // for error, event or element cx // count of x f // for flag nbr // for number tbl // for table
-
Use standard, neutral language.
kill
is better thandispatch
orslay
. -
Use positive Boolean names.
Prefer
isEnabled
overisNotDisabled
. -
Use stanard opposite pairs
Typical pairs include add/remove, begin/end, create/destroy, destination/source, first/last, get/release, increment/decrement, insert/delete, lock/unlock, minimum/maximum, next/previous, old/new, old/new, open/close, put/get,show/hide, source/destination, start/stop, target/source, and up/down.
-
Make names differ in meaning
Names like
input
andinputValue
, andrecordCount
andnumberOfRecords
can be easily confused if used in the same lexical context:val recordCount = ... ... val numberOfRecords = ... // Huh? We have this already
The name should express precisely what needs to be understood by the reader, nothing more. Avoid overspecifying meaning; treat names as kind-of generic interfaces - boil down the essence of what something is, identify and remove all that is not essential or can change. Leaking implementation is bad because it increases noise in the code and the coupling between different objects (in this case, between statements or expressions that use an overspecified term).
-
Keep name's meaning relevant to the semantics in its immediate context, nothing more. For example, if you're working with in-memory key-value database such as Redis, then probably your business logic shouldn't know all the details about its client object, such as that it is in-memory or Redis specficially. Then the
keyValueStore
would be a better name thaninMemoryKeyValueStore
orredisClient
. -
Omit type information, except for
is
prefix for Booleans. Generally, it's a good idea to avoid Hungarian like notations which leak the type of object, but the problem with Booleans such asisActive
is that "active" on its own can be confused with an adjective with an implied noun - "active something" instead of "is something active?". Booleans don't generally change types to non-Booleans, so it's a safer and more readable option.
-
is
should only return Boolean.Methods starting with
is
, e.g.isActive
should returntrue
orfalse
, nothing else, and should not produce side-effects. -
set
method shouldn't return anyhing.Setter methods imply that their result is a side-effect (of setting some variable) - returning something is confusing and counter-intuitive.
-
get
should return something.By convention, getter methods should always return something. Example violation: method
getImageData
sets the state of some global variable and doesn't return anything. -
Predicates should answer question.
Method names in form of a predicate (e.g.
isEnabled
) should return a Boolean, not anull
or an object. Example violation: attributeisReached
of typeint[]
where the declared type and values are not documented. -
Use Boolean variable names that imply true or false.
If method or a variable is of Boolean type, its name should suggest so, e.g.
isAuthorized()
. -
Don't use
get
,is
orhas
prefixes for functions with side-effects.Those words don't imply or suggest that side-effects can't happen, so the reader cannot assume that they will happen. If they do, it will probably be surprising to the reader.
-
Transform method should return anything.
Example:
apply_transformation_1(x)
return return transformedx
. -
Method name and behavior should be consistent.
For example, the result of calling function
disable(...)
should be a side-effect of disabling something, not, for example, returning object or function which can disable something. -
Attribute name and type should be consistent.
Example violation: attribute
startObject
is of typeObjectEnd
. -
Match plurality.
If an attribute is of collection type, its name should reflect that, e.g.
userNames: Set<String>
instead ofuserName
. Use plural nouns for function names which work on collections, e.g.convertDates(dates: List<Date>)
instead ofconvertDate
and return collections if name suggests so. -
Don't contradict comments. Keep them up to date.
- Arnaoudova, Venera, et al. “A new family of software anti-patterns: Linguistic anti-patterns.” 2013 17th European Conference on Software Maintenance and Reengineering. IEEE, 2013.
- Hilton, Peter, and Felienne Hermans. “Naming Guidelines for Professional Programmers.” PPIG. 2017
- https://github.com/Droogans/unmaintainable-code#naming
Great article. Just found a typo and since this is about naming and spelling (I know I'm reaching), thought to point out.