Software development is about discovering and encoding knowledge. One of the best ways that humans have for doing this is to name things and concepts. However, the chance of two people selecting the same name for a new idea or object is less than 10%! Natural language is imprecise. Code must be precise, but must also be human-comprehensible if developers are to read and modify it, and users are to receive helpful error messages.
To make shared understanding more likely, it's helpful to work with terms that are understood by technical and non-technical users, developers, sales teams, etc. Part of that is agreeing on a shared vocabulary, ideally with a glossary which is accessible to everyone. This is particularly important when developers will be working on a codebase over many years, or if the developers come from widely differing cultures, or have different first languages. All of these things increase the risk of misunderstanding, or even the risk that an abbreviated name may be offensive in another language or culture. See: I got fired over a variable name...
The fewer decisions you need to make, the more capacity you have for making good decisions when it really counts. This is one reason why linters and autoformatters, particularly those which come with generally accepted defaults, are so popular. Historically, linters have not be able to do much more than enforce conventions such as snake vs camel case, or no-numbers-in-names Large Language Models which integrate with code editors can be helpful, and are improving, so don't discount these tools completely when you are struggling to select a meaningful descriptor for a new abstraction.
- Learn the conventions of your language(s) and framework(s) and agree to follow them as a team
- Learn and understand the vocabulary of your domain, particularly as used by your customers/clients and sales people.
- Agree on and install a linter (Rubocop, Prettier, MarkdownLint, etc) and make CI passing conditional on linting success
- Agree on and install an autoformatter
- If your codebase allows (and doesn't have too many idiosyncracies) set up a 'format-on-save' hook
- If your codebase is in bad shape, run an autoformatter on any file you need to change before you change it, and add that as a preparatory commit.
- Agree on a set of steps to use when naming a new concept, and practice them
- Why are you creating this particular named object?
- How will you use this new thing?
- What are the expected results of this new method?
- Can you use an existing name from the codebase or problem domain?
- Can you create a derivative of an existing term?
- Can you use a widely-accepted abbreviation? (HTML is commonly understood, SDEP is not)
- Does the name communicate all the information needed to understand the purpose / use-case without reading the implementation?
- Are you storing up trouble by describing the same thing in different ways in different places in the codebase?
- Does the name include a word which has multiple potential meanings, depending on context? (bark, die, current, dispatch)
- Does the name include words which are esoteric or otherwise exclusionary? Would it be confusing to a non-native English speaker (flammable, inflammable, non-flammable)
- Does the name include words which have more than one accepted spelling? (synthesise, synthesize)
- Have you checked using a dictionary?
- Can you easily pronounce the name? Does it make sense in an English sentence? You will be talking about this thing.
- Create a paragraph-length precise definition of the new thing
- How is this entity different from the entities already in use in the context of the system?
- Have you been too specific, or tried to be too generic?
- Summarise your definition into a variety of four-word phrases, and review them against the criteria above.
- Consider if you can shorten the name without losing critical information.
- Share the name with non-technical as well as technical colleagues. Ask them what they think it is, or does.
- Consider: Is this one thing or many? Do you need to review the overall design? Has the class developed a gravitational field?
- Ask for help.
- Go for a walk.
- Use an obviously-nonsense name rather than something 'close enough' while you continue to work on the rest of the design and your understanding of the system. Applesauce, Badger, Mushroom, Snake, or something equally incongruous in the context of your domain.
- Naming Inspiration
- Naming conventions in programming – a review of scientific literature
- Your coding conventions are hurting you
- Execution in the Kingdom of Nouns
User story
SO THAT hosts can find companies they might like to invite to sourcing events WE WILL enable them to search Veridion's Complex Search API by product keyword
In order to do this, we need something to coordinate the the interaction between the host user making the search, the view(s) they see, and the data in and business logic around the search results. In other words, we need a Rails controller.
By convention rails controllers are in app/controllers, named in PascalCase with a name ending in Controller
. Usually the name of the controller is the same as that of the associated model, but pluralized.
Initial name ideas
VeridionComplexSearchController
VeridionController
VeridionEventParticipantSearchController
These names all contain useful information for a developer: in English, the controller interacts with the Veridion Complex Search API to return a collection of data about potential EventParticipants.
It does not, however, use terms that would be familiar to the sales team, or end users, and relies on developers knowing what Veridion offers. It will need to be changed if Veridion has a name change. It will need to be changed if the process of supplier discovery allows for e.g. fetching information about a named company, rather than searching.
The purpose of the controller is to enable our users to discover information about potential suppliers so they can decide whether or not to onboard the suppliers, or invite them to an event. It doesn't matter which third party provides the data, and it doesn't matter what the data will be used for.
The name selected was therefore SupplierDiscoveryController
We are not following the convention to use a plural name as it does not make for a gramatically logical name.
Context: An intractable bug where answers were being marked as failed when they were not, causing participants to be excluded from events.
The method QuestionnaireQuestionAnswer#failed_answer?
was misleading in several ways
failed_answer?
overrides a method provided by the Rails framework- this method, which matches the name of the model attribute
failed_answer
would be expected to query the attribute and return a Boolean. - the explanatory comment was also misleading (the attribute value was set, but not persisted)
This method is used to check if the answer is failed or not. It sets an attribute on the model called failed_answer which can be used in places where we just want to display the answer
What the method actually did was
- calculate whether the selected question choice was an autofail (killer) choice
- set the value of QuestionnaireQuestionAnswer#failed_answer to this value
- leave the modified QuestionnaireQuestionAnswer unsaved i.e. the change was not persisted
- return the Boolean value of the initial calculation
A more correct name (though objectively terrible) is calculate_and_return_autofail_question_answer_status_and_update_failed_answer_attribute_without_saving
This is too long for it to be a good name, but it does highlight that the method is doing more than one thing. It would be better to have one method which calculates, one method which updates, and one which queries the attribute
Split into three methods, we have calculate_autofail_questionnaire_question_answer_status
, get_boolean_failed_answer_attribute
and update_failed_answer_attribute_without_saving
Now we can work on improving each method name separately
calculate_autofail_question_answer_status
is correct and clear, but can still be improved.
As the method will always be called on a QuestionnaireQuestionAnswer, we do not need to include that information.
calculate_autofail_status
would be better, and easier to read and pronounce
update_failed_answer_attribute_without_saving
is standard Rails functionality and can be replaced by qqa.failed_answer = calculate_autofail_status
.
get_boolean_failed_answer_attribute
is also standard Rails functionality qqa.failed_answer?
With these changes made, it became clear that the failed_answer attribute was being saved at the wrong time. It was calculated and persisted whenever a questionnaire_question_choice was selected in a questionnaire_question_answer. It was not persisted when the questionnaire_answer (the collection of questionnaire_question_answer) was submitted and the failed_answer status was used to calculate if the entire questionnaire should be autofailed.
Changing the code to calculate and persist the failed_answer attribute on each questionnaire_question_answer when the entire questionnaire_answer was submitted, to calculate but not persist the attribute when a summary of unsubmitted questionnaire_answers was generated, and to neither calculate nor persist the attribute when a single question choice was selected resolved the bug.
CARE: These are Ruby-specific and may not apply to your language of choice
Choose dictionary words that clearly describe the purpose or use-case to avoid ambiguity
- Good:
heavy_goods_vehicle
,total_price_in_pence
- Bad:
rig
,total_p
Use terms that are familiar within the domain of the application, and consistent with end-user mental models.
- Good:
invoice_total
- Bad:
total_amount
Only use acronyms which are widely accepted and in common usage and avoid neologisms and slang
- Good:
http
,sim_dojo_event_participant
,unique
- Bad:
hyper_text_transfer_protocol
,sdep
,based
Use names which make sense when spoken as well as written to facilitate collaboration.
- Good:
modified_at
,unable_to_parse_file
- Bad:
mod_date_yymmdd
,file_unable_to_parse
Be specific to avoid confusion and make automated refactoring less risky
- Good:
customer_address
- Bad:
data
Ensure the name is not misleading (especially when changing code)
- Good: Contract attribute
alert_date
which stores the date on which the user wishes to be notified - Bad: Contract attribute
notice_period
which actually stores the date on which the notice_period starts when the name suggests an interval
Avoid homonyms (words which can mean different things depending on context)
- Good: explicit but not esoteric
contemporary
,alternating_current
,six-sided-die
,coining_die
- Bad:
current
(of the present time, moving electrical charge, moving water),die
(stop living, small cube with dots on the faces used in a game of chance, an engraved stamp used for e.g. coining money, and so on)
Choose one name for a concept and avoid synonyms
- Good:
current_user
- Bad:
active_user
,logged_in_user
,authenticated_user
,validated_session_holder
used interchangeably
Make booleans positive
- Good:
valid
- Bad:
not_invalid
Make names differ by more than word order
- Good:
employee_count
andcounter_staff
in the same context - Bad:
employee_count
andcount_employees
in the same context
Use singular nouns when naming single objects.
- Good:
product_sku
,EventParticipant
Use plural (or collective) nouns when naming collections
- Good:
contact_preferences
,fleet
(of company cars, ships, spacecraft etc)
Make names as long as they need to be to be clear, but no longer. A rule of thumb is four words, or 20 characters.
- Good:
participant_count
- Bad:
integer_number_of_participating_users
Avoid revealing the underlying implementation in the name
- Good:
AccountCreationData
- Bad:
AccountCreationDataJsonHash
Avoid using types or encodings as prefixes
- Good:
full_name
- Bad:
string_display_name
Name what a value represents, not what it is
- Good:
STANDARD_VAT_RATE = 20%
- BAD:
ONE_THIRD = 0.25
Avoid specifying the variable type in the name
- Good:
full_name
- Bad:
full_name_string
Use terms which are not an existing part of the framework or language.
- Good:
user_class
,app_module
- Bad:
class
,module
Do not override names/methods/concepts provided by the framework
- Good:
calculate_event_status
- Bad:
active?
(which does not return a boolean active attribute value but calculates and sets the status attribute)
Use snake_case for variables and methods, SCREAMING_SNAKE_CASE for constants, PascalCase for classes.
- Good:
user_name
,WAIT_TIME
,ApplicationController
- Bad:
userName
,WaitTime
,application-controller
Use verbs or verb phrases to indicate what the method does.
- Good:
calculate_total
,send_email
- Bad:
total
,email
Do not use conjunctions (and, or, but)
- Good:
calculate_invoice_discount
,calculate_invoice_total
,invoice_total=
- Bad:
apply_discount_and_total_invoice
- Edge Case:
find_and_replace
Avoid get_- and set_- prefixes.
- Good:
name
,name=
- Bad:
get_name
,set_name
Use predicate methods rather than is_-, is_not_-, or not_- prefixes when returning a Boolean
- Good:
active?
,locked?
- Bad:
is_activated
,is_not_locked
Use bang methods for dangerous methods such as those which are irreversible, may raise exceptions, or have side effects
- Good:
sort!
(modifies the existing list in place) - Good:
save!
(raises an exception if validation fails)
Research has found six main types of method name. It may be helpful to consider where your new name fits into this.
- indirect action, e.g.
error()
oron_error()
- direct action, e.g.
open()
,close()
,kill()
- action on object, e.g.
read_line()
,open_file()
- double action, e.g.
search_and_replace_text()
- property check, e.g.
is_active?()
oractive?()
- transformation, e.g.
convert_to_hex()
Use nouns, or noun phrases, that represent the entity or concept the class encapsulates.
- Good:
User
,ProcessedOrder
- Bad:
ProcessOrder
,Data
Avoid names which say nothing about the responsibility of the class, encouraging the dumping of random methods
- Good:
ConfirmedOrder
,AcceptedBid
,DateTimeLocalizer
- Bad:
ConfirmationManager
,DispatchHelper
orData
Use nested modules to group related classes in a way which matches their hierarchy (this is different to the concept of a Module as a mixin)
- Good:
Blog::Post
,Blog::Platform
;Bed::Post
,Bed::Platform
- Bad:
Post::Blog
,Post::Bed
,Post::Box
Do not duplicate the hierarchy in the class name
- Good:
Validators::Numeric
- Bad:
Validators::NumberValidator