Get to Completely Honest (Article 4)
I need to make my name fully describe everything this method does. Each level of improved naming should record more insights and make it easier for the next person to read the code.
This level actually makes it unnecessary for the next person to read the code. We want them to be able to trust that the name includes absolutely everything the method does so they can read and understand calling methods without having to read the method we’re fixing.
In the previous articles of this blog series, everything being done was accomplished in one step. However, being completely honest is hard so we need to take it in steps.
Each time you iterate on an honest name by making it more honest, you’ve made it easier on the next coder. A name might progress through this phase over the course of weeks or months. Eventually, though, the name will have become completely honest.
To complete “one more iteration” of the name becoming more honest, we look through the body of the method and find one of two things:
- Expand the Known: find one more thing that it does. Add it to the name.
- Narrow the Unknown: find one specific thing that we don’t know. Add it to the _AndStuff part of the name.
Expand the Known
The first option in your iteration of making the name more honest is simply finding one more thing that it does, which is explained in these three parts.
Part 1: Look inside the body
You know this by now. It is often useful to look for paragraphs or control structures.
Part 2: Look for one thing the code does that isn’t in the name
The process is:
- Find one thing that the code does (an effect, a calculation, a result, a state change).
- See if that is already included in the name (it might be a legitimate substep of something that is already in there).
- If not, add it (see Part 3).
Don’t broaden the parts of the name. Your goal is not to find one abstraction that includes everything the method does. Your goal is to be precise; a reader should be able to read the method name and know exactly what the method does.
Part 3: Write down by adding a clause to the name
Ignore everything you’ve been taught about naming conventions. Long names are good. Conjunctions are good. Belaboring the point is good. Your only goal is to make sure that everything this thing does is represented in the name. And therefore anyone can trust the name. They no longer have to read the thing, just its name.
Imagine emailing the method name to someone. Just the name. Would they be able to generate exactly all of the things in the method body? They shouldn’t want to add any more things and they shouldn’t miss any.
The purpose of the second section (middle) of the name is to simply record what is known. Recall from previous articles that probably_ flags the team that there is uncertainty. _AndStuff records what is not known, while the center lists everything that is known. So add your new clause to the middle section.
Narrow the Unknown
The second option in your iteration of making the name more honest is finding one specific thing that we don’t know, which is explained in these three parts.
Part 1: Look inside the body or at the name
If you’re looking inside the body, focus on data or parameters you haven’t analyzed yet. This will help you add a clause to the set of unknowns.
Another strategy to consider is to:
- Look at the name of the method,
- Pick one of the clauses of unknowns that you want to narrow, and
- Look at the parts of the method body that are related to that clause.
Part 2: Look for one category of things that the code doesn’t do
As the title suggests, you are looking for a category of things that the code doesn’t do, but also look for one partial truth for something the code does do. Let’s look at some examples.
Something that it doesn’t doAssume that I have a name _AndDoSomethingWithGraphicsContextAndStuff(). I realize that the method doesn’t write to the GC or use it to draw anything; it is read-only. I might name it to _AndDoSomethingReadOnlyToGraphicsContextAndStuff(). I don’t know what it does, but I’ve reduced the unknown.
A partial truth about what it does do
Assume my name includes _AndDoSomethingToDatabaseAndStuff(). I discover the code opens a write cursor and no reads, and rename to _AndWriteSomethingToDatabaseAndStuff(). I don’t yet know what is written, but I’ve exposed a partial truth and reduced the unknown.
The process is:
- Identify one piece of data related to what you’re looking for (that may be a variable, parameter, or field).
- Trace that data’s usage through the method to find one thing that is either done or not done with it.
- Update the name accordingly (see Part 3).
Part 3: Write down by adding a clause to the name
The purpose of the _AndStuff section of the name is to record and quantify the unknown behaviors of this method. That is why we start simply with _AndStuff. However, now that you are moving from an Honest Name to Completely Honest, we can provide more precision than _AndStuff. So now you are going to record the more precise information you have by adding a clause to _AndStuff.
Finishing the iteration towards Completely Honest
A Completely Honest name will include only the middle section.
To get rid of the third section, we have to eliminate the last unknowns. We will need the object to be so readable that we know for a fact it doesn’t do anything beyond what we’ve said. It is usually easiest to identify all the impacted data and generate specific clauses in the third section. Once we’ve traced all the data, we know there can be no other impacts, so we can remove the general AndStuff(). Then we simply have to chase down every unknown clause one at a time until the third section is gone.
To get rid of the first section we have to be totally confident in the rest of the name. The probably_ indicates that we are not yet sure. To become totally confident we will need two things. First, we will need tests that verify the method does everything we say it does in the middle section. Second, we will need the object to be so readable that we know for a fact all unknowns are scoped by the third section. Once both are true, we can eliminate the probably_. However, later editors then need to either be justifiably confident in their naming steps or re-add the probably_. For this reason, removing the probably_ is often the last step.
Really big methods
Sometimes understanding a subcomponent requires extracting it and getting it to have a better name. This is especially common with long methods. When you do this, keep your focus on the main name. Extract a chunk and record insights only until you know whether or not it is important for naming your main thing. Then update the main name and move on.
Guard clauses are a good example. They’re usually easy to identify but take up a bunch of space. I won’t actually use them in the name of the thing (most of the time), but they can make it hard to read the rest.
I’ll extract a bunch of what I think are guard clauses, get the name for the extracted component to be Honest, and see if they actually are all guard clauses. If so then I leave them alone and come back to the main method.
I’ll extract guard clauses until I’m left with the residual. Then I’ll analyze that more deeply and make sure I get everything into the name of my main method. I might inline the guard clauses again when I am done, or just leave them named something like _ValidateAllInputs().
In the example from the FAA issue I shared in Article 3, I needed to take several steps to find all the things my method was doing. I eventually identified 4 main chunks of functionality, each of which happened in many steps. So my method became parseXmlAndStoreFlightToDatabaseAndLocalCacheAndAddToScreenIfVisible().
Things that aren’t methods
This step also applies to variable and class names.
- Classes should be named by all of their individual responsibilities.
- Variable names should include all the ways the variable is used.
In deep legacy code variables may be used in a surprising number of ways. An example is once when I had a poor, overused in/out parameter that I had to name searchHintThenBestMatchSoFarUntilItBecomesReturnValueOrReasonNoMatchWasFound.
There is a strong design rule that says classes should have a single responsibility. That’s true. However, in this step we are ignoring what “should” be true and trying to simply be completely honest about what is true right now. Classes often have a primary responsibility while also including a couple of other responsibilities that mostly make sense and are too small to break out. That being said, this naming stage is simply being completely honest about all of those responsibilities. We will decide what to do about the multiple responsibilities in the next step.
Naming by what it does, not by what it is
Here the insights that I’m having are all the important characteristics of the thing I am naming. I’m naming it by what it does, not by what it is. This shift is a critical step in eliminating god classes and long methods.
**Named by what it is…**When a thing is named by what it is, then it accumulates everything vaguely related to that identity. Use this when you want other coders to naturally gather related pieces of functionality from other code and increase cohesion.
**Named by what it does…**When it is named by every single thing it does, then our desire for shorter names drives us to have it do less stuff. Use this when you want other coders to break it apart and simplify each chunk.
How something is named will drive the behavior of how people will engage with it and the future evolution of the method, variable, or class.
Help your team adopt Naming as a Process now!
Next up: use your completely honest name to guide the code to do what it should do.
Or read the whole Naming as a Process blog series.