- The Mistakes I Made As a Beginner Programmer
- By Samer Buna
- The Nuggets translation Project
- Permanent link to this article: github.com/xitu/gold-m…
- Translator: kezhenxu94
- Proofread by: DAA233 Dandyxu
Learn to recognize mistakes and make a habit of avoiding them
Let me be clear: if you’re a beginner programmer, this article is not meant to make you feel bad about the mistakes you might be making. It’s meant to make you aware of them, teach you how to recognize them, and remind you to avoid them.
I’ve made these mistakes a lot in the past, and I’ve learned a lot from each one. The good news is that I’ve developed good programming habits that will help me avoid making the same mistakes again. You should try to do the same.
The following errors are in no particular order.
1) Write code without a plan
Quality writing is not always easy to produce. It requires careful thought and research. High-quality program code is no exception.
There is one workflow to writing quality code: think. Investigation and research. Plan. Encoding. Validation. Modification. Unfortunately, this workflow doesn’t have a good acronym to help you remember. You need to develop good habits to go through the stages of your workflow, not one less.
One of the biggest mistakes I made in my early programming days was writing code without thinking and researching. While this can work on small, independent projects, it can have a serious negative impact on larger projects.
Just as you need to think twice before saying something you might regret, you need to think twice before writing code you might regret. Code is also a way to communicate ideas.
When angry, if you want to talk, count to 10. If very angry, count to 100.
— Thomas Jefferson
To paraphrase this:
When reviewing code, count to 10 if you want to refactor it. If there is no test code, count to 100.
— Samer Buna
Programming is mostly about reading existing code, researching new requirements and how they fit into the current system, and planning how to implement new features with testable incremental code. The actual time spent writing code is probably only 10% of the total process.
Don’t think of programming as writing code line by line. Programming is a logical creative process that needs to be nurtured.
2) Over-planning before writing code
Yes. A little planning before diving into code is a good thing, but even that can backfire. Drinking too much water can poison you.
Looking for the perfect plan in the programming world? It doesn’t exist. Look for a plan that’s good enough to get you started. Planning can always catch up with change, but it can push you in an organized direction, which will make your code clearer. Too much planning is just a waste of time.
What I’m talking about now is only planning for small features, and trying to plan all of them at the beginning is even worse! This is called a waterfall model in software engineering, and is a systematic linear plan in which each step is completed in sequence. You can imagine how much planning this waterfall model requires. This is not the type of plan discussed here. The waterfall model is not effective in most software projects. Anything complex can only be flexibly adjusted to reality.
Programming must be a responsive process. You’ll add new features that you never thought of before, which you couldn’t imagine in waterfall, and you’ll remove features for reasons you never thought of before. You need to fix bugs to adapt to change. You need to be flexible.
That said (don’t overplan), be sure to plan for the few new features that follow. Plan carefully, because inadequate or excessive planning can compromise the quality of your code, and you don’t want to risk it.
3) Underestimating the importance of code quality
If you could only focus on one aspect of your code, it would be readability. Code that doesn’t mean anything is garbage, even garbage that can’t be recycled.
Never underestimate the importance of code quality. Think of writing code as a way to communicate implementation. The primary job of a programmer is to clearly communicate the implementation of the current solution.
One of my favorite quotes about programming is:
Always code as if the person who ultimately maintains your code is a violent freak who knows where you live.
– John Woods
That’s wise advice, Jonh!
Even the smallest details matter. For example, if you indenting and capitalization are inconsistent in your code, you deserve to lose your programming license.
tHIS is
WAY MORE important
than
you think
Copy the code
Another point is about the use of long lines. Any line of code longer than 80 characters is much harder to read. You might try to put some very long conditional judgments on the same line to make the if statement clearer, don’t do that. Never make a line of code more than 80 characters long, ever.
Many of these simple problems can be fixed using linting and formatting tools. There are two great tools that work perfectly together in JavaScript: ESLint and Prettier. Do yourself a favor and put them to use.
Here are some other code quality myths:
-
Write too many lines of code in a method or file. You should always break up long code into smaller pieces for testing and individual management. I personally think anything above 10 lines is too long, but that’s just a rule of thumb.
-
Use double negatives. Please, please don’t don’t not do this.
It is not infallible to use double negatives.
- Use short, generic, or type-based variable names. Give your variables descriptive and unambiguous names.
There are only two hard things in computer science: clearing caches and naming.
– Phil Karlton
- Hardcode string literals and number literals without description. If you need to write code that relies on fixed literal string values or numeric values, use constants to hold those fixed values and give a good variable name.
const answerToLifeTheUniverseAndEverything = 42;
Copy the code
-
Use sloppy shortcuts and gimmicks to avoid spending more time on simple problems. Don’t dance with problems, face reality.
-
The longer the code, the better. In most cases, however, the shorter the code, the better. Write long code only if the code is more readable. For example, do not embed a large number of ternary operators (? 🙂 to make code shorter. Of course, don’t intentionally make your code unnecessarily long. Removing unnecessary code is one of the best things you can do on any project.
Measuring programming progress by lines of code is like measuring aircraft construction progress by weight.
– Bill Gates
- Overuse of conditional logic. Most situations where you feel you need conditional logic can be done without it. Consider all alternatives and choose only one of them based on readability. Don’t optimize performance unless you can already measure it. Related: Avoid Yoda conditions and assignment by condition.
4) Use the primary plan
I remember when I first started programming, when I had a problem, I could find a solution and jump right into it. I rushed to implement it without thinking about the complexity of the initial solution and the potential for failure.
Tempting as the initial solution may be, the best solution is often found when you start exploring multiple alternatives. If you can’t come up with multiple solutions to the problem, chances are you don’t fully understand the problem.
As a professional programmer, your job is not to find a solution to a problem, but to find the simplest solution to the problem. By “simple solution,” I mean that the solution must be accurate, perform well, and be easy to read, understand, and maintain.
There are two ways to build a software design. One is to make it simple enough that there are obviously no flaws, and the other is to make it complex enough that there are no obvious flaws.
– C.A.R. Hoare
5) Don’t give up
Another mistake I often make is that I stick with my initial solution, even after I’ve confirmed that it may not be the easiest solution. This is probably what psychology calls the “don’t give up” mentality. This is a good state of mind in most activities, but not in programming. In fact, when it comes to programming, the right mindset is to fail as quickly as possible and as often as possible.
When you begin to doubt a solution, you should consider throwing it away and rethinking the problem. No matter how much effort you’ve put into the solution. Take advantage of a version control system like GIT, which can help you manage and experiment with different solutions separately.
Don’t get obsessed with it just because you put a lot of effort into it. Bad code should be discarded.
6) Don’t Google
There are countless instances where I spent precious time trying to solve a problem when I could have gotten results from simple research in the beginning.
Unless you are using cutting-edge technology, chances are that when you encounter a problem, someone else has already encountered the same problem and found a solution. Save yourself some time by googling first.
Sometimes a Google search may reveal the fact that what you think is a problem is not, and that what you need to do is not fix it, but embrace it. And don’t think you know everything you need to know to find a solution. Google will surprise you.
Still, you need to be careful when you Google. The mark of a novice is to copy and paste someone else’s code without understanding it, and while it may solve your problem correctly, you should never use even one line of code that you don’t fully understand.
If you want to be a creative programmer, don’t assume you know what you’re doing.
As a creative person, the most dangerous idea is to think you know what you’re doing.
– Bret Victor
7) No encapsulation
This point is not just about the object-oriented paradigm. It is always useful to use encapsulation. Not using encapsulation often results in systems that are difficult to maintain.
In an application, a function should only be handled in one place, which is usually the responsibility of an object that exposes the necessary interfaces only to other objects that need to use it. It’s not about confidentiality, it’s about reducing the interdependence between different parts of an application. Sticking to these rules allows you to safely change the internal implementation of your classes, objects, and functions without worrying about breaking the wider world outside of where you changed them.
Logical units and states of concepts should have their own classes. By class I mean a blueprint template, which may indeed be a class object, or a function object, or you can think of it as a module or a package.
Within a logical unit, self-contained task blocks should have their own methods, and a method should only do one thing and do it well. Similar classes should use the same method name.
As a junior programmer, I often had trouble writing a new class to organize conceptual units, and often couldn’t figure out what was self-contained. If you see an “Util” utility class, it’s a garbage dump with lots of disconnected code, and that’s the nature of novice code. If you make a small change and find that it has a ripple effect that requires a lot of other changes, that’s another characteristic of new code.
Think about it before adding a method to a class or more responsibilities to a method, and ask your intuition. This is where you need to take your time. Don’t skip it or think “I’ll refactor it later.” Do it early on.
The basic idea is that you want your code to be highly cohesive and low coupled, which is just a fancy term for keeping related code together (in a class) and reducing the interdependence between different classes.
8) Plan for the unknown
There is often a tendency to think beyond the current solution being written. Every line of code has all sorts of “what ifs” in your head. This is good practice when testing boundary cases, but it is a mistake to use it as a driver of potential requirements.
You need to identify which of the two categories your “what ifs” fall into. Don’t write code you don’t need right now. Don’t plan for the unknown future.
It’s an obvious mistake to write code to implement a feature because you think you’ll use it later. Don’t do that.
Write as little code as you can for the scheme you are implementing. Of course, handle boundary cases, but don’t add boundary functionality.
Growth for growth’s sake is the mind of cancer cells.
– Edward Abbey
9) Not using proper data structures
Entry-level programmers often focus too much on algorithms when preparing for interviews. It’s nice to be able to identify good algorithms and use them when needed. But memorizing these algorithms won’t improve your programming skills.
On the other hand, memorizing the pros and cons of the various data structures in your programming language will definitely make you a better developer.
Using the wrong data structure is a huge and obvious billboard that says “This is beginner’s code”.
This article is not intended to teach you about data structures, but here are a few quick examples:
– Use lists (arrays) instead of mapping tables (objects) to manage records
The most common data structure mistake is using lists rather than mapping tables to manage a list of objects. Yes, you should use a mapping table to manage a list of records.
Note that the list of records I’m discussing here is one in which each record has a unique identifier that can be used to find objects. Using lists to manage scalar values is fine and often a better choice, especially if the key usage is to “push” some values into a list.
In JavaScript, the most common list structure is an array, and the most common mapping table structure is an object (there are mapping table structures in modern JavaScript as well).
Using lists instead of mapping tables to manage objects is usually a mistake. Although this is true when you’re managing a lot of records, I’d say keep doing it. The main reason this is important is that maps are much faster than lists when it comes to finding objects using unique identifiers.
– No stack is used
When writing code that requires a recursive form, it’s often easy to use simple recursive functions. However, optimizing recursive code is often difficult, especially in a single-threaded environment.
Optimizing recursive code depends on what the recursive function returns. For example, optimizing a recursive function that returns more than two calls to itself is much harder than optimizing a function that returns only one call to itself.
What we often overlook as beginners is that there are alternatives to recursive functions. You can use stack data structures. The result of the function call is pushed on the stack manually and then popped off the stack when needed.
10) Make existing code worse
Suppose you were given a messy room like this:
Now you are asked to place an object in this room. Since the room is already a mess, it’s easy to just leave things out and be done in a matter of seconds.
Don’t do this when you’re dealing with messy code. Don’t mess up the code any more! Always leave the code a little cleaner than when you started.
What you should do in the above room problem is to clear out the parts needed to make room for the new items. For example, if it’s an item of clothing that needs to be put in the closet, you’ll need to clear a path out of the closet. This is part of getting the task right.
Here are some bad practices that often make code worse than before (incomplete list) :
- Copy the code. If you copy/paste code and change only one line, you are duplicating code and making it worse. In the example of a messy room above, you take a chair with a lower base rather than consider using a chair with adjustable height. Always keep an abstract concept in mind and use it whenever you can.
- No configuration file is used. If you’re going to use a value that might not be the same in other circumstances, or at other times, that value should be in the configuration file. If you need to use a value in different parts of your code, this value should also be placed in the configuration file. When introducing a new value into your code, just ask yourself: should this value be in the configuration file? The answer is probably “yes”.
- Use unnecessary conditional statements and temporary variables. each
if
Statements are a branch of logic that needs to be tested twice. When you can avoid conditional statements without sacrificing readability, you should do so. The main question here is whether to use branching logic to extend one function or to introduce another. Every time you feel the needif
Statement or a new function variable, you should ask yourself: am I changing the code at the right level, or should I be thinking about it at a higher level?
For unnecessary if statements, look at the following code:
function isOdd(number) {
if (number % 2 === 1) {
return true;
} else {
return false; }}Copy the code
There are several problems with the isOdd function above, but can you spot the most obvious one?
He used an unnecessary if statement. Here’s the equivalent:
function isOdd(number) {
return (number % 2 === 1);
};
Copy the code
11) Comment code that is obvious
Now I’ve learned how to do everything I can to avoid the problems I face when writing comments. Most comments can be replaced with better-named elements.
For example, don’t write code like this:
// This function sums only odd numbers in an array
const sum = (val) => {
return val.reduce((a, b) => {
if (b % 2 === 1) { // If the current number is even
a+=b; // Add current number to accumulator
}
return a; // The accumulator
}, 0);
};
Copy the code
The same code can be rewritten without comments like this:
const sumOddValues = (array) => {
return array.reduce((accumulator, currentNumber) => {
if (isOdd(currentNumber)) {
return accumulator + currentNumber;
}
return accumulator;
}, 0);
};
Copy the code
Just better naming functions and arguments can save you a lot of comments, so keep that in mind before writing comments.
However, sometimes you may be forced into situations where adding comments is the only way to improve the clarity of your code. At this point you should organize your comments to answer why the code is used rather than what the code does.
If you feel the urge to write a comment explaining “what this code does” to increase code clarity, don’t write the obvious. Here’s an example where useless comments only add to the code’s intrusiveness.
// Create a variable and initialize it to 0letsum = 0; ForEach (sum += number; sum += number; sum += number; });Copy the code
Don’t be a programmer like that. Don’t accept such code either. I deleted those comments when I had to deal with them. If you happen to hire the programmer who wrote those comments, fire him, now.
12) No tests are written
I’ll make this point briefly, but if you think you’re an expert programmer and that idea gives you the confidence to write code without testing, you’re a novice in my vocabulary.
If you’re not writing tests in code, you’re probably testing your code in some manual way. If you’re building a web app, every time you change a few lines of code you have to refresh in the browser and do some interaction to test it again. That’s what I did. There’s nothing wrong with manually testing your code, but you should use manual testing to figure out how to automate testing. If you test an interaction in your application, you should automate the test code for that interaction before you add more.
If you’re a human being, it’s hard to guarantee that you’ll be able to do all the tests you’ve done before every time you change the code. Let the computer do it for you.
If possible, even before you start writing code to implement requirements, you should begin to anticipate and design the situations that need to be tested and validated. Test-driven Development (TDD) is not just some fancy hype, it can actually have a positive impact on how you think about features and design better solutions.
Test-driven development (TDD) doesn’t work for every person and every project, but if you can use it (even in one part of your project), you should definitely do it.
13) When code works, it works
Take a look at sumOddValues, the function that adds all odd numbers. Any questions?
const sumOddValues = (array) => {
return array.reduce((accumulator, currentNumber) => {
if (currentNumber % 2 === 1) {
return accumulator + currentNumber;
}
return accumulator;
});
};
console.assert(
sumOddValues([1, 2, 3, 4, 5]) === 9
);
Copy the code
The test claims pass. Life is good. Really, really right?
The problem is that the code above is incomplete, and in a few cases it works correctly, and the assertions used by the test happen to be one of those cases, but there are many other problems. Let’s name a few:
– Problem 1: null input is not processed. What if the function is called without passing any arguments? In this case, an error is generated that exposes the internal implementation of the function.
TypeError: Cannot read property 'reduce' of undefined.
Copy the code
This is usually a sign of bad code for the following reasons:
- The user of the function should not see the implementation of the function.
- The error message is of no help to the user, and a function that doesn’t work doesn’t work. However, if the function’s error message were more specific about how the function is being used, users of the function might know that they are using it incorrectly. For example, you can choose to have the function throw a custom exception like this:
TypeError: Cannot execute function for empty list.
Copy the code
Instead of throwing an exception, you can also redesign the function to ignore empty input and return 0. Either way, you should do something in this situation.
– Problem two: No exception input is processed. What happens if instead of passing an array, a function is called with a string, an integer, or an object?
Now the function throws an error like this:
sumOddValues(42);
TypeError: array.reduce is not a function// (array.reduce is not a function)Copy the code
Well, that’s unfortunate, because array.reduce is definitely a function!
Since we named the function’s arguments array, any argument that calls the function (42 in the above example) will be labeled array inside the function. This error means that 42.reduce is not a function.
You see for yourself how puzzling a mistake like that is, don’t you? Perhaps the more helpful mistake is this:
TypeError: 42 is not an array, dude. // TypeError: 42 is not an array, dude.Copy the code
Problem one and problem two are sometimes called boundary cases, there are some basic boundary cases to consider, but often there are some less obvious boundary cases to consider as well. For example, what if we pass negative numbers as arguments?
SumOddValues ([1, 2, 3, 4, 5, -13]) // => 9Copy the code
Well, minus 13 is also an odd number. Is this the behavior you expected? Should I throw an exception? Should negative numbers also be summed? Or is it doing what it’s doing now, just ignoring negative numbers? By now you probably realize that the name of this function should have been called sumPositiveOddNumbers.
It’s easy to make a decision in this case, but more importantly, if you don’t write a test case documenting the reason for your decision, future maintainers of the function may have no idea whether you’re intentionally ignoring negative numbers or if there’s a bug.
It’s not a bug, it’s a feature.
— Forgot to write the test code
– Problem 3: Not all valid cases are tested. Regardless of the boundary case, there is another legal, very simple case of this function that is not handled correctly:
sumOddValues([2, 1, 3, 4, 5]) // => 11
Copy the code
These 2’s are also being added to the sum, but they shouldn’t be.
The simple answer is that Reduce takes another parameter as the initial value of the summer. If this parameter is not passed (as in the code above), the reduce function starts the summer with the first value of the set. That’s why the first even number in the example above will also be summed.
Although you may be when you start writing code immediately aware of the problem, but exposed the problem of test cases should be included in the first test set, together with many other basic test cases, such as “all the transfer is an even number of array”, “transmission contains 0 array”, and “empty array”.
If you see a small number of test cases and haven’t handled most or none of the boundary cases, that’s another sign of new code.
14) Not questioning existing code
Unless you’re a solo super coder, you’ll undoubtedly encounter some pretty stupid code in your life. Beginners often can’t tell the difference between good and bad code, and often think it’s good code because it seems to work and has been in the repository for a long time.
Worse, if bad code uses bad practices, beginners are likely to have applied the bad practices elsewhere in the repository because they learned them as good code.
Some of the code looks bad, but there may be special circumstances that force developers to write it, and this is where detailed comments should be written to tell beginners about the special circumstances and why the code is written the way it is.
As a beginner, you should always assume that code that you can’t read and isn’t documented is probably bad code. Use git blame to find the culprit!
If the author of the code has been away for a long time, or if he can’t remember it, study the code and try to understand everything about it. Only when you fully understand the code can you establish a sense of how good or bad the code is, and don’t assume anything until then.
15) Obsession with best practices
I think “best practice” is actually harmful, it implies that you don’t need to dig into it, it’s the best practice ever, no doubt!
There is no such thing as a best practice; there may be good practices for the language that exist today.
Some things that were previously considered best practices in programming are now labeled as worst practices.
If you put in enough time, you can always find better practices. Don’t worry about best practices and focus on what you can do best.
Don’t do it because you’ve read about it somewhere, don’t do it because you’ve seen others do it, don’t do it because they say it’s best practice, including all the advice in this article! Question everything, challenge authority, know all possible options, and make informed decisions.
16) Obsessed with performance
Premature optimization is the root of all evil
– Donald Knuth (1974).
Although computer programming has changed dramatically since Donald Knuth wrote that statement, I think it’s still valuable advice today.
Here’s a good rule to remember: If you can’t measure a suspected performance problem in your code, don’t try to optimize it.
If you’re optimizing code before you run it, chances are you’re optimizing code too early, and you’re spending a lot of time and effort optimizing it unnecessarily.
Of course, there are some obvious optimizations that you should consider before introducing new code. In Node.js, for example, it is critical not to flood events or block the call stack. This is an example of early optimization that you should always keep in mind. Ask yourself: Is this part of the code I’m considering blocking the call stack?
Any non-obvious optimizations in unmeasured existing code are harmful and should be avoided as much as possible. You may feel like it’s a performance benefit when done, but it turns out to be the source of new and unexpected bugs.
Don’t waste time optimizing unmeasured performance issues.
17) Not targeting the end user experience
What’s the easiest way to add a feature to an app? Look at the feature from your perspective, or see how the new feature fits into the current user interface, right? If the new feature is to get input from the user, add it to your existing form. If the new feature is to add a link to a page, add it to your existing menu list.
Don’t be that developer. Be a professional developer and see things from the end user’s point of view. Professional developers should consider what users need and how to use a particular feature, and try to make it easy for users to discover and use, rather than trying to find the most convenient way to add the feature in the application, regardless of the discoverability and availability of the feature.
18) Not picking the right tools for the task
Each person has a list of favorite tools that assist them in their programming related activities. Some tools are good, some are awful, but most are good at one particular task and not so good at anything else.
A hammer is an excellent tool for driving nails into walls, but it’s the worst tool for turning screws. Don’t use a hammer to turn a screw just because you love it. Don’t screw it just because it’s a popular hammer with a 5.0 rating on Amazon.
Choosing a tool based on its popularity rather than its applicability to a problem is the mark of a true novice.
One problem with this is that you may not know the “better” tools for a particular task. In your current knowledge, this tool may be the best you know. However, it is not the first choice when compared to other alternatives. You need to educate yourself about the tools available and be open to new tools that you might use later.
Some programmers are reluctant to use new tools. They are happy with the tools they are using and don’t want to learn new tools. I understand that, but it’s clearly wrong.
You can build a house with primitive tools and have a good time. You can also spend some time and money learning about advanced tools to build better houses faster. Tools are constantly evolving, and you should be willing to learn them and use them.
19) Not understanding code problems can lead to data problems
An important aspect of a program is usually managing some form of data. A program is an interface for adding new data, deleting old data, and modifying existing data.
Even the smallest bug in a program can cause the data it manages to fall into an unpredictable state. Especially when all data validation is done entirely in this buggy program.
When it comes to code-data relationships, beginners may not immediately understand the connection. They may not think it’s a big deal to continue using the buggy code in production because the broken feature X is not super important. The problem is that this buggy code continues to create data integrity problems, which may not be obvious at first.
To make matters worse, deploying bugfix code without fixing minor data problems caused by bugs can cause more data problems to accumulate and eventually “accumulate.”
What can you do to protect yourself from these problems? You can simply use multiple layers of data integrity validation. Do not rely on a single user interface. Data verification is performed at the front end, back end, network layer and data layer. If you can’t do that, at least do it in the database layer.
Familiarize yourself with database constraints and use them as much as possible when adding tables and columns to your database:
- NOT NULL The NOT NULL constraint indicates that the NULL value cannot be saved to the column. If your application assumes that the value of this column exists, you should define the column as non-null in the database.
- The UNIQUE constraint means that all values on that column cannot be repeated throughout the table. For example, this is an excellent use scenario for username fields and mail fields in user tables.
- The CHECK constraint is a custom expression that evaluates to true for all data to be accepted by the database. For example, if you have a regular percentage column with a value between 0 and 100, you can use the CHECK constraint to ensure this.
- The PRIMARY KEY constraint means that the value of this column must be both non-null and unique. You’re probably already using it. Each table in the database should have a primary key that uniquely identifies a record.
- FOREIGN KEY A FOREIGN KEY constraint means that the value of this column must match the value of a column (usually the primary KEY) in another table.
Another problem with data integrity for novices is the lack of a transactional mindset. If multiple operations that change the same data source depend on each other, they must be included in the same transaction so that they can be rolled back if one of them fails.
20) Repeat the wheel
This is a tricky point. In programming, some wheels are simply worth reinventing. Programming is not a well-defined domain. So many things change so fast and new requirements are introduced faster than any team can handle. This is a tricky one. In programming, some wheels are worth reinventing. Programming is not a well-defined field. So many things change so quickly and new requirements are introduced so quickly that no one team can handle it perfectly.
For example, if you need a wheel that spins at a different speed depending on the time of day, maybe instead of modifying the wheel we know and love, we should think about rebuilding it. However, unless you really need a wheel of an unconventional design, don’t build a new wheel from scratch. Just use the damn wheel.
Sometimes it can be challenging to choose the right wheel from the many brands available. Research and use before you buy! The cool thing about the wheels is that most of them are free and open, and you can see inside them. You can easily judge a software wheel by the quality of its internal design. If possible, use open source wheels. Open source software packages are easy to debug and fix. It can be easily replaced. Plus, you can support them without leaving home.
But if you need a wheel, don’t go out and buy an entire car and put the one you’re maintaining on top of the new one. Don’t introduce an entire code base just to use one or two functions; a classic example in JavaScript is the LoDash code base. If you want to shuffle an array randomly, just introduce the shuffle method. Don’t introduce the whole loadash code base, it’s scary.
21) Wrong attitude towards code review
One sign of novice programmers is that they often view code reviews as criticism. They don’t like code reviews, they don’t appreciate code reviews, and they dread them.
This is wrong. If you feel this way, you need to change your attitude now. Treat every code review as a learning opportunity, welcome them, appreciate them, learn from them, and most importantly, thank your code reviewers when you learn from them.
When it comes to programming, you’re always a learner. Acknowledge it. Most code reviews will teach you something you didn’t know before. Use them as learning resources.
Sometimes code reviewers make mistakes, and it’s your turn to teach them. But if the problem isn’t obvious from your code alone, maybe in that case your code needs to be changed accordingly. If you need to teach your reviewers a lesson anyway, remember that teaching is one of the most rewarding activities for programmers.
22) No version control system is used
Novices often underestimate the power of a good version control system, and by good version control I mean Git.
Source control is not just about you pushing your changes out to others to build on, but there’s more to it than that. Source control is about clear history. The source code can be questioned, and the history of the code can help answer these difficult questions. That’s why we care so much about submitting information. They are also another way to communicate implementation, and using commit information can help future maintainers understand why the code is in its current state.
It should be submitted often and early. For consistency, use the simple present tense form of the verb in the subject line of your commit message. Always be careful that the information you submit is as detailed as possible, but also as summative as possible. If you need more than a few lines to write your submission, it’s likely that your submission contains too much, Rebase!
Don’t include anything extraneous in your submission. For example, don’t list which files you added, modified, or deleted. These files are included in the commit object itself and can easily be displayed using Git command parameters. Some teams like to write a summary message for each file change, which I think is a sign of “too much submission.”
Source control is also about discoverability. If you see a function and start to question its necessity and design, you can find the commit record that introduced the function and see the context of the function. Submitting records can even help you identify which code is causing bugs. Git even provides a tool (bisect command) for binary lookups between commits to help locate bug-introducing commits.
Source control can also be used in magical ways, even before changes are officially committed. Tools such as staging changes, selective patching, reset, staging, modify, apply, compare, withdraw, and more provide a wealth of tools for your workflow. Know them, learn them, use them, appreciate them.
In my dictionary, the less you know about Git, the more you’re a beginner.
23) Overuse of shared state
Again, this is not a discussion of functional programming and other paradigms; that is a topic for another article.
The fact is that shared states are a source of problems and should be avoided if possible, and used to a minimum if not avoidable.
When I was a junior programmer, I often didn’t realize that each variable I defined represented a shared state that held data that could be modified by all elements in the same scope. The larger the scope of a shared state, the longer its span. Try to contain new states in small scopes and make sure they don’t escape.
The big problem with shared state is that in the same Event loop (for event-loop-based environments), an error occurs when multiple resources need to modify the state at the same time. This is when race conditions occur.
Here’s the thing: a novice might try to use timers to solve the problem of race conditions for shared variables, especially if they have to deal with a data lock. Don’t do it. Pay attention to it. Point it out in code reviews.
24) Wrong attitude in the face of Error
Error is a good thing. It means you’re making progress, and it means you can make more progress with simple follow-up modifications.
Professional programmers love errors. Novices hate Error.
If seeing these stunning red Error messages bothers you, you need to change your attitude. You need to think of them as assistants. You need to deal with them, you need to use them to improve.
There are some errors that need to be improved to exceptions. Exceptions are user-defined errors that you plan for. Some errors don’t need to be handled; they should crash and exit.
25) No breaks
You are alone and your mind needs to rest. Your body needs rest, too. You’re often so in the zone that you forget to take a break. I take this as another sign of a novice. This is not something you can compromise on. Integrate something into your workflow to force you to take a break. Take lots of short breaks, get out of your chair and take short walks to figure out what to do next, and come back to the code with clear eyes.
This is really a long article. You should get some rest.
Thank you for reading
The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.