I’m sharing some habits and rules that you can start practicing today to become a better developer and write better code. These are valuable lessons I’ve learned over the years from regretting the things I’ve written. After all, the worst coder I know is the one I was six months ago.
Let’s focus on the details
No one likes boring, tedious work. But good senior developers are willing to do boring work to make sure their codebase behaves consistently. Good senior developers set clear, understandable, and reasonable rules and stick to them. They are extremely sensitive to indentation and use linters or code formatters to keep their code formatted consistently. They are sensitive to variable capitalization, even if the language they are using is case-insensitive. They always use curly brackets around statements, even if they are not required.
Basically, great senior developers know that if they pay attention to the small details, the big parts will often take care of themselves.
Good naming
There’s an old joke that says, “The two hardest things in software development are cache invalidation, naming, and one-digit errors.” Naming is hard, but most problems can be avoided by knowing how to avoid bad names.
First of all, choosing a short name to reduce the hassle of typing is a very wrong way. Short variable names in abbreviation are a remnant of the past when the terminal window was 80 characters wide. Now, you should use grossWeight instead of gw, and netWeight instead of nw. There is no disadvantage to doing this. If you are worried about the hassle of typing, you can use IntelliSense.
Along with not using abbreviations, you should try to make the name as complete as possible. There is no reason to just call it length when you could have a more complete name like lengthInCentimeters.
There is no downside to long, clear variable names that clearly describe the variable’s purpose.
Always use explanatory variables
Beginner coders often look for shortcuts, sometimes without even realizing that they are taking shortcuts. So they often write code like this:
“`
function checkAccess(userRole: string, isAuthenticated: boolean, isAdmin: boolean, hasSubscription: boolean, isGuest: boolean): string {
return ((isAuthenticated && userRole === “member”) ||
(isAdmin && !hasSubscription) ||
(isGuest && !isAuthenticated && userRole === “guest”) ||
(isAuthenticated && userRole === “premium” && hasSubscription))
}
“`
My brain, which is not very smart, has a hard time keeping track of more than two boolean expressions at once. Instead of just thinking about it and writing down the first thing that comes to mind, it is much better to name each boolean expression step by step, group them into a single value, and then use it in an if statement.
“`
function checkAccess(userRole: string, isAuthenticated: boolean, isAdmin: boolean, hasSubscription: boolean, isGuest: boolean): string {
const isMemberAndAuthenticated = isAuthenticated && userRole === “member”;
const isAdminWithoutSubscription = isAdmin && !hasSubscription;
const isGuestAndNotAuthenticated = isGuest && !isAuthenticated && userRole === “guest”;
const isPremiumMemberWithSubscription = isAuthenticated && userRole === “premium” && hasSubscription;
const hasAccess = isMemberAndAuthenticated ||
isAdminWithoutSubscription ||
isGuestAndNotAuthenticated ||
isPremiumMemberWithSubscription;
return hasAccess;
}
“`
Using explanatory variables like this makes it much easier to follow the entire Boolean expression. Using explanatory variables also makes it easier to see the various elements in the debugger.
Always take one step at a time and use explanatory variables to create clear and understandable code.
Coding for abstraction
The key to writing good code is functionality. Great senior developers know that there is always room for improvement in how they do things, so they code to some kind of abstraction rather than a concrete implementation.
The idea is that “radical new implementations” can be discovered at any time, but if we are too tied to a specific implementation, it is difficult to take advantage of those new solutions.
By defining an interface and coding accordingly, you can easily leverage new implementations without missing them.
Make and test only one change at a time
After some trial and error, you eventually find that when you make more than one change and test the new code and a problem occurs, you can’t be sure which change caused the problem.
Smart developers follow the concept of working slowly and carefully, one step at a time, systematically changing one thing at a time, checking the impact of that one change, and then moving on to the next. This is especially important in large codebases with a lot of interdependencies, because a change in one place can have unexpected effects somewhere else. When something unexpected happens, you need to be able to immediately know which change caused the problem.
Coding so that any task is performed in only one place
A good code maintainer should never have to worry about figuring out which of the three INSERT statements is actually updating the database. That kind of work should only happen in one place.
Senior developers don’t write code to do anything anywhere. If there is a configuration setting, the work of reading and writing the configuration should be done in only one place. If you need to update the state of a customer, you should write code for the Customer object, not there.
Also, if you need to change the behavior of any part of your application, you should only change it in one place. If the same behavior occurs in three places, you have to change all three places to change the behavior you want, and changing three places means two more places where something can go wrong.
Keep the size from growing
Big chunks are the building blocks of bad code. Big classes and big methods are the antithesis of good code. Everyone has seen huge classes and methods that are hundreds or thousands (!) of lines long. A good rule of thumb I follow is that lines should not exceed 3 lines when collapsed.
It’s also been said that if you can’t see the entire method on one screen in the editor, it’s time to refactor.
The most basic thing to do in this case is to use a “guard clause,” commonly called an inversion. A common cause of deep nesting is a large number of nested if statements. By reversing the “ask if we can continue” pattern into an “if condition is met, exit” pattern, you can avoid many of the complex Boolean situations. Inversion often eliminates all nesting in a method.
Another strategy is to always extract code into smaller methods so that no block of code becomes too large. Senior developers are not afraid of having lots of small classes and methods. Of course, all of these classes should have descriptive names.
Don’t comment your code
I left this for last because I know many developers will be upset. But I am firm on this point. I firmly believe that 99.9% of comments are a sign of bad code, bad names, or a lack of explanatory variables. If you feel like your code needs comments, it is most likely because it is unclear and hard to understand. If your code is unclear and hard to understand, you should rewrite it until it is clear and easy to understand. (Good naming and using explanatory variables help.)
Also, comments are not physically connected to the code they point to, so they can drift elsewhere and cause endless confusion.
When is it appropriate to comment code? In some cases, for example, code is often optimized to use unusual or uncommon algorithms for performance reasons. In such cases, when there is a valid reason to write non-obvious code, code comments are necessary.
There are many more rules that a senior developer should follow, but if I were mentoring a new developer, I would emphasize the seven methods introduced here. They are based on hard-won knowledge gained through experience and are easy to implement. Unfortunately, in most cases, what upgrades a senior developer to a great developer is nothing more than bad code experience.
editor@itworld.co.kr
Source: www.itworld.co.kr