惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
Lohrmann on Cybersecurity
Martin Fowler
Martin Fowler
Engineering at Meta
Engineering at Meta
腾讯CDC
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Microsoft Azure Blog
Microsoft Azure Blog
G
Google Developers Blog
TaoSecurity Blog
TaoSecurity Blog
博客园_首页
Vercel News
Vercel News
Hugging Face - Blog
Hugging Face - Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Last Week in AI
Last Week in AI
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
T
The Exploit Database - CXSecurity.com
量子位
Project Zero
Project Zero
A
Arctic Wolf
小众软件
小众软件
NISL@THU
NISL@THU
C
CERT Recently Published Vulnerability Notes
有赞技术团队
有赞技术团队
MongoDB | Blog
MongoDB | Blog
博客园 - 聂微东
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
N
News and Events Feed by Topic
宝玉的分享
宝玉的分享
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
T
Troy Hunt's Blog
P
Privacy & Cybersecurity Law Blog
Security Latest
Security Latest
B
Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
D
DataBreaches.Net
Schneier on Security
Schneier on Security
The Hacker News
The Hacker News
K
Kaspersky official blog
C
Check Point Blog
Hacker News: Ask HN
Hacker News: Ask HN
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Webroot Blog
Webroot Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
人人都是产品经理
人人都是产品经理
AI
AI
Cisco Talos Blog
Cisco Talos Blog
MyScale Blog
MyScale Blog
Cloudbric
Cloudbric
B
Blog RSS Feed
S
Schneier on Security
P
Palo Alto Networks Blog

Dmitri Pavlutin Blog

Pure Functions in JavaScript: A Beginner's Guide Record Type in TypeScript: A Quick Intro How to Write Comments in React: The Good, the Bad and the Ugly 4 Ways to Create an Enum in JavaScript React forwardRef(): How to Pass Refs to Child Components TypeScript Function Types: A Beginner's Guide How to Use v-model to Access Input Values in Vue Mastering Vue refs: From Zero to Hero Environment Variables in JavaScript: process.env 5 Must-Know Differences Between ref() and reactive() in Vue How to Destructure Props in Vue (Composition API) Triangulation in Test-Driven Development How to Use nextTick() in Vue Programming to Interface Vs to Implementation A Smarter JavaScript Mapper: array.flatMap() Array Grouping in JavaScript: Object.groupBy() How to Access ES Module Metadata using import.meta JSON Modules in JavaScript How to Trim Strings in JavaScript TypeScript Function Overloading How to Debounce and Throttle Callbacks in Vue How to Show/Hide Elements in Vue Sparse vs Dense Arrays in JavaScript How to Fill an Array with Initial Values in JavaScript Covariance and Contravariance in TypeScript What are Higher-Order Functions in JavaScript? How to Use TypeScript with React Components Index Signatures in TypeScript How to Use React useReducer() Hook unknown vs any in TypeScript A Guide to React Context and useContext() Hook How to Use Promise.any() 2 Ways to Remove a Property from an Object in JavaScript 'return await promise' vs 'return promise' in JavaScript How to Use Promise.allSettled() How to Use fetch() with JSON JavaScript Promises: then(f,f) vs then(f).catch(f) What is a Promise in JavaScript? How to Use Promise.all() A Simple Guide to Component Props in React Don't Stop Me Now: How to Use React useTransition() hook A Simple Explanation of JavaScript Variables: const, let, var ES Modules Dynamic Import How to Memoize with React.useMemo() How to Cleanup Async Effects in React Why Math.max() Without Arguments Returns -Infinity How to Debounce and Throttle Callbacks in React Don't Confuse Function Expressions and Function Declarations in JavaScript How to Use ES Modules in Node.js Solving a Mystery Behavior of parseInt() in JavaScript How to Use Array Reduce Method in JavaScript 3 Ways to Merge Arrays in JavaScript A Guide to Jotai: the Minimalist React State Management Library The Difference Between Values and References in JavaScript How to Implement a Queue in JavaScript A Helpful Algorithm to Determine "this" value in JavaScript React useRef() Hook Explained in 3 Steps 7 Interview Questions on "this" keyword in JavaScript. Can You Answer Them? How to Greatly Enhance fetch() with the Decorator Pattern 7 Interview Questions on JavaScript Closures. Can You Answer Them? What's a Method in JavaScript? array.sort() Does Not Simply Sort Numbers in JavaScript How to Solve the Infinite Loop of React.useEffect() The New Array Method You'll Enjoy: array.at(index) What's the Difference between DOM Node and Element? Why Promises Are Faster Than setTimeout()? Everything About Callback Functions in JavaScript How React Updates State 5 Mistakes to Avoid When Using React Hooks 5 Best Practices to Write Quality JavaScript Variables Type checking in JavaScript: typeof and instanceof operators 3 Ways to Check if a Variable is Defined in JavaScript React Forms Tutorial: Access Input Values, Validate, Submit Forms Prototypal Inheritance in JavaScript How to Timeout a fetch() Request How to Learn JavaScript If You're a Beginner A Simple Explanation of React.useEffect() A Simple Explanation of JavaScript Iterators How to Use React Controlled Inputs Everything about null in JavaScript How to Use Fetch with async/await Getting Started with Arrow Functions in JavaScript An Interesting Explanation of async/await in JavaScript Front-end Architecture: Stable and Volatile Dependencies Is it Safe to Compare JavaScript Strings? How to Access Object's Keys, Values, and Entries in JavaScript What Actually is a String in JavaScript? 3 Ways to Shallow Clone Objects in JavaScript (w/ bonuses) Checking if an Array Contains a Value in JavaScript JavaScript Event Delegation: A Beginner's Guide How to Parse URL in JavaScript: hostname, pathname, query, hash 3 Ways to Detect an Array in JavaScript How to Get the Screen, Window, and Web Page Sizes in JavaScript 3 Ways to Check If an Object Has a Property/Key in JavaScript How to Compare Objects in JavaScript Object.is() vs Strict Equality Operator in JavaScript Own and Inherited Properties in JavaScript 5 Differences Between Arrow and Regular Functions How to Use Object Destructuring in JavaScript Your Guide to React.useCallback()
Coding like Shakespeare: Practical Function Naming Conventions
Dmitri Pavlutin · 2016-10-12 · via Dmitri Pavlutin Blog

A clear and meaningful prose is easy to read and follow. Everyone enjoys reading such prose.

The same quality should apply to the source code. The way the developer expresses his thoughts through a programming language is important. Writing code is communication: with your teammates and yourself.

Clean code practicing is a big topic. So let's start with small steps.

The current article covers the functions/methods naming good habits. Functions are the moving parts of the application, so having named them precisely increases the readability.

The scope is to understand what the function does from its name, arguments list and the way it is invoked. If you have to jump into function details to understand what it does, probably the name is not precise or it obscures the intent. Let's see how to pass such situations.

1. The importance of readability

The amount of code of a complex application is enormous. Thousands of lines of code, hundreds of methods and classes. The meaningful and easy to interpret code is obligatory in big applications if you don't want to get lost in the jungle.

Unfortunately, every developer faced the problem of poor code readability. Everyone can remember spending a lot of time understanding the reason behind an obfuscated code.

When reading someones else's poorly named methods, classes, variables, you spend much more time decrypting the code than writing new functional. But it shouldn't be that way, and there is no excuse in writing such this way.

Coding activity chart

The average developer spends 75% of the time understanding code, 20% of the time modifying existing code and only 5% writing new (source).

A slight additional time of a single developer spent on readability reduces the understanding time for teammates. This practice becomes important when the application size grows since the understanding time increases with complexity.

Reading meaningful code is easy. Nevertheless, writing meaningful code is the opposite: you have to learn clean code practices and put constant effort to express yourself clearly.

2. Prefer explanatory names

The name should clearly, without ambiguity indicate what the function does. You don't have to jump around searching for the truth.

Function names should apply the lower camel case form: addItem(), saveToStore() or getItemById().

Every function is an action, so the name should contain at least one verb. For example:

  • write(to: "filename.txt") means write to file
  • reloadTableData() means to reload table data
  • isBirthDay() means checking if today is birth day
  • getAddress() or setAddress("My Address") mean getting or setting an object's address.

Let's study the following structure methods:


import Foundation

struct Pair {

var first: Int

var second: Int

func getAbsoluteDifference() -> Int {

return abs(first - second)

}

mutating func increase(by increaseAmount: Int) {

first += increaseAmount

second += increaseAmount

}

}

var myPair = Pair(first: 2, second: 5)

myPair.getAbsoluteDifference() // => 3

myPair.increase(by: 2)

myPair.first // => 4

myPair.second // => 7


The method name getAbsoluteDifference() means to get the absolute difference between the pair of numbers. The method name contains a verb get. It would not make much sense to name the method simply absoluteDifference(), because such name does not indicate any action.

The same rule applies to increase() method. Its name also contains a verb increase, which expresses what action it performs: increase the structure numbers by an amount.

I like the Swift inference mechanism that allows to significantly shorter the declarations.

But in the case of naming, clarity is more important than shortness. It's useless to name the methods in previous example getAbsDiff() or inc(). In such a case, you have to mentally restore the full function name to realize the meaning.

Moreover, what could the short inc() mean? Increment, increase or include? You have to read carefully the invocation context or even the method body to understand what it does. Avoid such ambiguous abbreviations.

In the meantime try not to make the name too verbose. Extended names like getAbsoluteDifferrenceOfTwoNumbers() or increaseByAmount() don't add anything useful to readability.

Balance between short and verbose in function naming

My advice is to find a balance between short and verbose in naming, and put accent on clarity:

Not too short, not too verbose: Just exactly what it does.

I don't see a problem if a function is renamed multiple times to find the best name that describes it. A fresh view over an old function may help to find a better name.

3. Use natural language with argument labels

Not sure if someone is happy to read the assembly language. Such language is natural for computer hardware, but quite hard to process by the human brain.

The textual information is easier to process in a natural language form: good old English. Write your code as a detailed and interesting story.

Be Shakespeare! (why not?)

Write code as an interesting story

The machine doesn't care how you feed it with instructions.

But your colleague developer does care. As it turns out, the code in the form of natural language is easier to understand. Make your code as short, concise and interesting stories. Variables are nouns and functions are verbs.

Swift provides additional goodies. When calling a function, specify the argument label that indicates more expressively the intent of the argument: myObject.action(role1: value1, role2: value2, ...). Naturally such call may sound: in myObject perform an action using value1 that indicates role1, value2 that indicates role2.

Let's follow an example. The Swift array methods provide great meaning about the the operation over the array:


var elements = [1, 5, 7]

elements.insert(8, at: 1)

elements // => [1, 8, 5, 7]

elements.remove(at: 2)

elements // => [1, 8, 7]


elements.insert(8, at: 1) invocation flows naturally when reading it: in elements insert 8 at position 1.
The same natural flow applies to elements.remove(at: 2): in elements remove the item at position 2.

In both cases notice the readability improvement that the argument label at adds: expressing the position when the operation should apply.

When the argument label is omitted, the ambiguity increases.

In the case of elements.insert(8, 1), what does 8 and 1 mean? What is the position and what is the element to insert? Hard to guess without diving into method details or reading the documentation.

4. Avoid pointless function names

Obviously, the production code should never contain functions named without strict meaning. Or named just for the sake of naming.

Some examples of pointless function names:

  • foo(), bar(), baz(), buzz() - without reasonable sense
  • fun(), myFun(), function(), procedure() - the nonsense obvious

These have no meaning and should be avoided. You can always find the correct name for any type of function.

Often such pointless function names are used in samples:


func foo() -> String {

return "bar"

}

foo() // => "bar"


In my opinion, correct naming practices should propagate even is such simple examples. Pointless function names create bad habits, which are hard to correct later.

The following alternative form looks better:


func greet() -> String {

return "Hello, world"

}

greet() // => "Hello, world"


Precautions should be taken with general action names like do(), execute(), run(), go() and so on. Such naming may fit only when the context is expressive: directoryCopyCommand.execute().

5. Avoid the same name covering multiple concepts

The function name indicates a well-determined type of action. It should be a one-to-one relation.

If the same name represents many equivalent still different actions, it might be covering too many concepts and decreases readability. One-to-many — one name that covers many actions should be avoided.

For example an application has in different classes a method named get(). Having the same name, these methods do different things: fetches data from server httpService.get(), gets the wrapped object wrapper.get() or initialize and get a storage instance Storage.get().

These concepts are different, and should have their own distinguishable names. Correspondingly httpService.fetch(), wrapper.getWrappedValue() and Storage.initialize() function names are better.

6. Avoid the same concept covered by many names

The same concept can be expressed using different words. These are synonyms. For example refresh or reload, delete or remove, etc.

In the articles of this blog, I use synonyms to make the writing easier to follow. When a word appears too often, the reading flow is not comfortable because of repetition. I search a synonym for repeated word and apply it.
For instance, I often replace the bothersome word use with apply.

When writing code, follow the opposite approach: one concept using only one word, and avoid synonyms.

For example each of the following function name pairs describe a single concept: add() or append(), remove() or delete(), refreshTable() or reloadTable(), include() or require(), and so on.
You have to select one name for a concept and use it consistently across the entire application.

Let's see the following scenario:


let homeNumber = PhoneNumber("555-555")

person.phoneNumbers.append(homeNumber)

// ...

let child = Person(firstName: "Anna", lastName: "Smith")

person.children.add(child)


At first sight, the code looks good.

However a more detailed review could wonder why person.phoneNumbers uses append() method and person.children uses add()? These methods perform the same action, and there is no reason to name them differently.

person.children.add() method should be refactored to append() name.

If you're not sure which word to choose for a specific function, try the following steps:

  1. Verify the application for functions with the same behavior. Use the matched function name
  2. If the above didn't work, verify the standard libraries for the same behavior functions. You can even find a protocol with required function signatures: go ahead and implement the protocol
  3. In other cases choose the most appropriate and descriptive word from available synonyms.

7. Don't say one thing and do another

Most of the time a function is a part of one of the following categories:

  • Answers a question or provides information: payment.isProcessed(), names.contains("Alex")
  • Changes the state of the object: person.setAddress("Earth"), names.append("Victoria"), view.reloadSubviews()
  • Executes a task and returns the result: name.characters.reversed(), names.sorted(), numbers.getAverage().

The function name also should stick to one of these 3 categories.

A readability problem appears when the function is named according to one category. But the function implementation does much more than that.

For example, a function is named as changing the object state and unexpectedly answers the operation result. Let's see the problem:


class LimitedCollection {

let maxNumberOfItems = 4

var items = [Int]()

init(items: [Int]) {

self.items = items

}

func add(_ item: Int) -> Bool {

if (items.count < maxNumberOfItems) {

items.append(item)

return true

}

return false

}

}

let collection = LimitedCollection(items: [1, 2, 3, 4])

if (collection.add(5)) {

print("Successfully added new item")

} else {

print("Cannot add items: collection is full")

}

// => "Cannot add items: collection is full"


The name of the method add() pretends to add items into the collection, changing the state of the object. And unexpectedly it returns the insertion result.

add word doesn't say anything about the collection fullness state. It might be unexpected to see add method in a conditional statement:
if (collection.add(5)) { ... }.

You might rename the method to something that really says what it does: addAndGetFullnessState().

Or a better option is to split the function into specialized functions. One verifies the collection fulfillment and another simply adds items to the collection if possible. Let's see the changed version:


class LimitedCollection {

let maxNumberOfItems = 4

var items = [Int]()

init(items: [Int]) {

self.items = items

}

func isFull() -> Bool {

return items.count >= maxNumberOfItems

}

func addIfPossible(_ item: Int) {

if (items.count < maxNumberOfItems) {

items.append(item)

}

}

}

let collection = LimitedCollection(items: [1, 2, 3, 4])

if (collection.isFull()) {

print("Cannot add items: collection is full")

} else {

collection.addIfPossible(5)

print("Successfully added new item")

}

// => "Cannot add items: collection is full"


collection.isFull() clearly indicates a verification of the collection fullness. This method answers a question, as the name isFull says.
collection.addIfPossible(5) adds items to collection if possible. This method changes the state of the object, as the name addIfPossible says.

8. Conclusion

Applications development is about communication. The quality of the communication directly affects the development productivity.

Reading meaningful code is easy and enjoyable. However writing quality code requires efforts, practice and constant review.

As a part of the code readability, function names have an important place. Looking at any code, most of the expressions are actually function/method calls.

A function name should clearly indicate what the function does. You don't have to scroll around, open the function source code to understand how it works. Also, the name should describe only one concept: one-to-one relation.

In the end, you probably don't want to communicate like these guys!

What is your opinion about the code readability and function naming? Feel free to write a comment below!