Naming things, which is most of the job
There is an old joke that the two hard problems in computer science are cache invalidation, naming things, and off-by-one errors. The joke is about the third one. The serious part is the second.
You will spend far more time reading code than writing it — your own included, six months later, with no memory of writing it. Names are the interface between what the code does and what the next person believes it does, and when those two drift apart, that is where bugs live.
A name should say what, not how
// how
List<Delivery> filterListByDateRangeUsingStream(List<Delivery> list, YearMonth m)
// what
List<Delivery> deliveriesIn(YearMonth month)
The second survives changing the implementation. The first is a comment about the current implementation, written where a comment cannot be ignored, and it becomes a lie the moment somebody rewrites the body.
Length should match scope
A short name is fine when its whole life is visible:
for (Delivery d : deliveries) {
total += d.tiffins();
}
d is perfectly readable — it is born and dies within two lines. But a field
living for the lifetime of an object, read from twenty places, needs to carry
its meaning with it:
private final Map<String, Subscriber> subscribers; // good
private final Map<String, Subscriber> m; // not
The further a name travels, the more it has to carry. A loop variable, three characters. A private field, a word. A public API method, as many words as it takes.
Say what the units are
This is the single highest-value naming habit in this course, and the capstone is built on it:
long pricePaise
long amountPaise
int tiffinsPerDay
Duration timeout
price alone raises a question the reader has to answer from somewhere else:
rupees or paise? pricePaise cannot be misread, and the day somebody writes
pricePaise = 285 instead of 28_500, the name is standing right there
arguing with them.
The same goes for time (timeoutMillis, retryAfterSeconds), size
(maxSizeBytes) and anything with a scale. A name carrying its unit is a bug
that cannot be written.
Booleans read as questions
if (subscriber.isPaused()) { … }
if (order.hasDeliveryAddress()) { … }
if (delivery.canBeCancelled()) { … }
is, has, can, should. The test is whether if (name) reads as English.
if (subscriber.paused()) is fine; if (subscriber.pause()) reads as a command
and will be misread as one.
And avoid negatives:
if (!subscriber.isNotActive()) // two negatives, one confused reader
if (subscriber.isActive()) // same thing
Say no to the noise words
Some words add characters and no meaning:
DeliveryData // as opposed to a Delivery that is not data?
DeliveryInfo
DeliveryObject
processDelivery // "process" means nothing
handleDelivery
doRecordDelivery
manageDeliveries
Manager, Helper, Util, Processor, Handler, Data, Info, Object.
If removing the word does not change the meaning, remove it.
The exception is a genuine convention: DeliveryStore says where deliveries
live, and BillingService says this does billing work and holds no data of
its own. Those carry meaning. DeliveryHelper does not.
A class you cannot name is usually a class doing more than one thing. That is a design problem showing up as a naming problem, and it is worth listening to.
Be consistent about one idea
Pick one word per concept and use it everywhere:
// Pick one:
getDelivery / fetchDelivery / retrieveDelivery / loadDelivery
// and one:
customer / client / subscriber / user
The capstone says customer in Delivery and Subscriber for somebody with a
plan — and those are genuinely different things, which is exactly when two words
are right. Two words for the same thing makes a reader search for a
distinction that is not there.
The JDK is consistent and you can lean on it: size() for collections,
length() for strings, length for arrays. That inconsistency is a historical
accident everybody has had to memorise — a good example of the cost.
Java's conventions are not optional
| Thing | Convention | Example |
|---|---|---|
| Class, interface, record, enum | UpperCamelCase, a noun |
BillingService |
| Method | lowerCamelCase, usually a verb |
billPaise |
| Variable, field | lowerCamelCase, a noun |
tiffinsPerDay |
| Constant | UPPER_SNAKE_CASE |
MAX_TIFFINS_PER_DAY |
| Enum constant | UPPER_SNAKE_CASE |
Plan.STUDENT |
| Package | all lowercase, no underscores | com.riztech.tiffin |
| Type parameter | one capital letter | T, K, V, R |
| Test method | whatever reads as a sentence | rejectsAPincodeStartingWithZero |
These are not matters of taste in Java the way they are in some languages. Every codebase you join will follow them, every tool assumes them, and departing from them just marks the code as written by somebody who did not know.
Interfaces are not prefixed with I. That is a C# habit. In Java the
interface gets the good name — List — and implementations are specific:
ArrayList, LinkedList.
Test names are documentation
@Test void test1() // useless
@Test void testPincode() // what about it?
@Test void rejectsAPincodeStartingWithZero() // now the failure report reads
When a build fails at 9pm, the test name is the first and often only thing you see. Write it so that reading it tells you what broke.
Names as a design signal
Naming difficulty is information. Listen to it.
- "I cannot name this method" — it probably does two things. Split it and the names appear.
- "I need
andin the name" —saveAndNotifyis two methods. - "I keep writing
Manager" — the responsibility is not clear yet. - "The name is very long" — either the method is doing too much, or its context is missing and it belongs on a different class.
// hard to name
void processDeliveryAndUpdateInvoiceAndNotify(Delivery d)
// easy to name
void record(Delivery d)
void updateInvoice(Delivery d)
void notifyCustomer(Delivery d)
Check your work
Why names matter more than they look: you read far more code than you write, and a name is what the next person believes the code does.
Why to name what, not how: a name describing the implementation becomes a lie when the implementation changes.
How length relates to scope: the further a name travels, the more it must carry — three characters in a two-line loop, a full phrase in a public API.
The highest-value habit here: put the unit in the name — pricePaise,
timeoutMillis. A name carrying its unit is a bug that cannot be written.
How booleans should read: as a question, with is, has, can or
should, and never as a double negative.
Which words to delete: Manager, Helper, Util, Data, Info,
process, handle — if removing it changes nothing, remove it.
Why two words for one idea is worse than a bad word: the reader searches for a distinction that does not exist.
Why Java's conventions are not taste: every codebase and every tool assumes them.
Why interfaces have no I prefix: the interface gets the good name and
implementations are specific.
What naming difficulty tells you: a method you cannot name is doing two
things, and and in a name is a split waiting to happen.
Practice
- Open your capstone and find the three worst-named things in it. Rename them and see whether anything reads better.
- Find every variable holding money in your code. Confirm each has
Paisein its name; fix the ones that do not. - Find a boolean method that does not start with
is,has,canorshould. Read a call site aloud. - Search your code for
Manager,Helper,Util,Data,Info. For each, try deleting the word. - Find two words used for the same concept in one codebase. Pick one and change the other.
- Look at your test names. Rewrite three so the failure report alone tells you what broke.
- Find a method whose name contains
and. Split it. - Rename a loop variable from
dtodelivery. Decide whether it improved. - Ask somebody to guess what one of your methods does from its name alone.
- Find a method you struggled to name. Work out what it was telling you.
Next: how big is too big.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship