📚WHAT I'VE LEARNED AS A JUNIOR🤓
Understand what types of mistakes you tend to make and create routines to help you avoid making them.
I want to be clear that this article is not meant to make you feel bad if you are a beginner programmer and make mistakes. Instead, I want to help you recognize mistakes, learn from them, and form habits to help you avoid them in the future. I have made these mistakes in the past and have learned from them, and I want you to do the same. The mistakes are not listed in any particular order.
1. Writing code without making a plan first
Creating high-quality written content, such as programs, is not easy and requires careful thought and research. There is a process to follow when writing programs: think, research, plan, write, validate, and modify.
It is important to create a habit of going through all of these steps in order to avoid making mistakes. As a beginner programmer, I have made the mistake of starting to code without thinking and researching first, which can have a negative effect on larger applications.
Just like you need to think before speaking, you need to think before coding in order to communicate your thoughts effectively.
When angry, count to 10 before you speak. If very angry, a hundred. — Thomas Jefferson.
Here is my version of that quote:
Before you make changes to a line of code, take a few seconds to think about it. If the code does not have any tests, take even more time to consider your changes.
Programming is much more than just writing lines of code; it involves reading existing code, researching what is needed and how it fits into the current system, and planning out the development of features in small, testable steps.
The actual writing of code is only a small part of the process. Programming requires creativity and logical thinking to be successful.
2. Thinking about what you want to do before you start writing the code
It is important to plan before writing code, but don't overdo it. Look for a good-enough plan that will help you get started, but don't expect it to be perfect. The plan will likely change as you go along.
Don't use the Waterfall Approach, which is a linear plan with distinct steps that must be completed one by one. Instead, be agile and responsive to changes. Make sure to plan your next few features carefully, as too little or too much planning can both hurt the quality of your code.
3. Not giving enough attention to how well the code is written
If you are writing code, make sure that it is easy to understand. Poorly written code is useless and cannot be fixed. Do not underestimate the importance of writing good code. Think of coding as a way to explain how something works. Your main goal as a coder should be to clearly explain the solutions you are working on.
One of my favorite quotes about programming is:
Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live. — John Woods
Even the smallest details are important. For example, if you don't use the correct indentation and capitalization when writing code, you should not be allowed to code.
tHIS is
WAY MORE important
than
you think
It is important to keep lines of code short, no more than 80 characters. Do not try to fit long conditions on the same line to make the code more visible. Instead, use linting and formatting tools like ESLint and Prettier to help you keep your code organized and easy to read.
Here are a few more mistakes related to code quality: When writing code, it is important to use descriptive and non-ambiguous variable names instead of short, generic, or type-based names. Additionally, it is best to break long code into smaller chunks that can be tested and managed separately, as functions or files with more than 10 lines can be difficult to manage.
There are only two hard things in Computer Science: cache invalidation and naming things. — Phil Karlton
If you need to use a fixed string or number in your code, create a constant and give it a descriptive name instead of just using the string or number without any explanation.
const answerToLifeTheUniverseAndEverything = 42;
Don't try to take shortcuts or find workarounds to avoid spending more time on simple problems. Don't try to make your code longer than it needs to be, but also don't try to make it shorter with clever tricks. Make sure to delete any unnecessary code.
Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates
Using too many if statements. Most of the things you think need if statements can be done without them. Look at all the options and choose the one that is easiest to understand. Don't try to make it faster unless you can measure the difference. Also, try to avoid writing if statements in a strange order and don't assign values inside if statements.
4. Choosing the first Solution
When I first started programming, I would quickly jump to a solution without considering the complexities and potential issues of my first idea. The best solutions are usually found when you question all the solutions you come up with.
If you can't think of multiple solutions to a problem, it's likely that you don't fully understand it. As a professional programmer, your job is not to find any solution, but to find the simplest solution that works correctly and performs well, but is still easy to read, understand, and maintain.
There are two ways of constructing a software design. One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. — C.A.R. Hoare
5. Not giving up
I have made the mistake of sticking with the first solution I come up with, even when I know it might not be the best approach. This is probably because I don't like to give up. While this is a good attitude to have in most activities, it is not the best approach when it comes to programming. It is better to fail early and often when writing programs.
If I start to doubt a solution, I should throw it away and start over. I should not be attached to code because of how much effort I put into it. I can use tools like GIT to help me experiment with different solutions.
6. Not using the search Google first
When I have a problem, I should not waste time trying to solve it on my own. Instead, I should research it online first. Chances are, someone else has already encountered the same problem and found a solution.
Additionally, I should be careful not to assume that I know the best solution to the problem. I should be open to the possibility that the solution I think of might not be the best one. Finally, I should never copy and use code that I do not understand. If I want to be a creative coder, I should never think that I know what I'm doing.
The most dangerous thought that you can have as a creative person is to think that you know what you’re doing. — Bret Victor
7. Not Using Encapsulation
When writing code, it is important to keep related logic and state together in a class and to reduce the dependencies between different classes. This means that each feature should have one place that handles it, usually the responsibility of a single object. This object should only reveal what is necessary for other objects to use it.
Additionally, each class should have methods that do one thing and do it well. If you find yourself making a lot of changes to different parts of the code when making a simple change, this is a sign that the code is not organized properly.
To ensure that the code is organized properly, it is important to take the time to think and question your instincts before adding a method to a class or adding more responsibilities to a method.
8. Preparing for unexpected events
It is easy to think of extra features that you might need while writing code, but it is important to remember that you should only write code for what you need right now. Do not plan for the future and do not write code that you do not need.
Stick to writing the minimum amount of code necessary for the current solution and handle any edge cases that come up.
Growth for the sake of growth is the ideology of the cancer cell. — Edward Abbey
9. Not Using the Right Data Structures
When you are getting ready for job interviews, it is common for beginner programmers to focus too much on algorithms. It is important to know which algorithms are good and when to use them, but memorizing them will not make you a better programmer. Instead, it is more beneficial to memorize the strengths and weaknesses of the different data structures that you can use in your language. Using the wrong data structure is a clear sign that you are a beginner.
This article is not meant to teach you about data structures, but here are a few examples:
- Instead of using objects to store a list of records, some people mistakenly use lists (arrays). This is usually not the best choice, especially when the records need to be looked up by their identifier. Maps (objects) are much faster for this purpose. It's best to always use maps for managing records, even if the list is small.
- When writing code that requires recursion, it is often tempting to use simple recursive functions. However, it can be difficult to optimize recursive code, especially in single-threaded environments. To make it easier to optimize, it is better to use a Stack structure instead of recursive functions. Push function calls to a Stack and pop them out when you need to traverse the calls back.
10. Making changes to existing code that make it worse
You were asked to add an item to a room that is already messy. Don't just put the item anywhere and be done with it in a few seconds. Instead, take the time to clean up the mess so that you can place the item in the right place.
For example, if the item is a piece of clothing, make sure to clear a path to the closet so that you can put it away properly. Avoid making the code messier by not following any of the wrong practices, such as not organizing the code or not commenting on the code.
- Duplicating code: If you copy and paste a section of code and only change one line, you are creating more mess and not solving the problem. It's like having a messy room and instead of buying a chair that is adjustable, you buy another chair with a lower base. Always think of ways to simplify the code and use them when you can.
- Not using configuration files: If you need to use a value that could change in different situations or at different times, you should put it in a configuration file. If you need to use the same value in multiple places in your code, it should also be in a configuration file. Whenever you add a new value to your code, ask yourself if it should be in a configuration file - the answer is usually yes.
- Writing code that includes extra if statements and variables that are not needed: Every time you want to use an if-statement, you should make sure to test it twice to make sure it works correctly. Whenever possible, try to avoid using conditionals as they can make the code harder to read. The main issue with using if-statements is that it can make it difficult to extend the function. Before adding an if-statement or a new function variable, ask yourself if you are changing the code at the right level or if you should look at the problem from a higher perspective.
Consider if it is necessary to use an if-statement in this code, as it may not be necessary.
function isOdd(number) {
if (number % 2 === 1) {
return true;
} else {
return false;
}
}
Can you spot the problem with the isOdd function above? The code includes an extra if-statement that is not needed. Here is a simpler version of the same code:
function isOdd(number) {
return (number % 2 === 1);
};
11. Writing remarks or opinions about things that are already known or easily understood
I have learned that it is better to use descriptive names for variables, functions, and classes instead of writing comments to explain what the code does. This makes the code easier to understand and reduces the need for comments.
// 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 odd
a+=b; // Add current number to accumulator
}
return a; // The accumulator
}, 0);
};
The same code can be written without comments like this:
const sumOddValues = (array) => {
return array.reduce((accumulator, currentNumber) => {
if (isOdd(currentNumber)) {
return accumulator + currentNumber;
}
return accumulator;
}, 0);
};
When writing code, it is best to use clear names for functions and arguments so that comments are not necessary. However, if you need to add comments, make sure they explain why the code is written the way it is, not what it does. Do not add comments that are obvious or unnecessary, as this will just add noise to the code.
// create a variable and initialize it to 0
let sum = 0;
// Loop over array
array.forEach(
// For each number in the array
(number) => {
// Add the current number to the sum variable
sum += number;
}
);
Do not tolerate code or comments that are disrespectful or offensive. If you encounter such comments, delete them. Make sure to explain to the programmer who wrote them why they are inappropriate and that they could be fired for writing them. It is that serious.
12. Not Writing Tests
If you think you are an expert programmer and don't need to write tests for your code, you are wrong. Even if you are manually testing your code, you should still write tests to make sure that the same validations are performed each time you add more code. Testing-driven development (TDD) is a great way to think about your features and design them better. It may not work for every project, but if you can use it, you should.
13. If things are working, then they are correct
Examine this code that adds up all the odd numbers. Is there any mistake in it?
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
);
The statement is true and life is good. Is that correct? The code is not finished and does not work properly in many situations. For example, the assertion used in the code works, but there are other issues that need to be fixed.
Problem #1: If the function is called without any arguments, there should be a way to handle it so that an error revealing the function's implementation does not occur.
TypeError: Cannot read property 'reduce' of undefined.
The error message that the user sees when they use your function incorrectly is not helpful, which is a sign of bad code. This is because users should not be aware of the implementation details of the function, and the error message should be more clear about the usage problem so that they know they used the function incorrectly. To make the error message more helpful, you can choose to have the function throw a user-defined exception.
TypeError: Cannot execute function for empty list.
If your function receives empty input, you should design it to ignore the input and return a result of 0 instead of throwing an error. You need to do something to handle this situation.
Problem #2: What should happen if the function is called with something other than an array, such as a string, an integer, or an object?
Here is what the function would throw now:
sumOddValues(42);
TypeError: array.reduce is not a function
That is unfortunate because the argument 'array' is not a function, so it cannot use the .reduce method.
TypeError: 42 is not an array, dude.
Problems #1 and #2 are special cases that are often mentioned. These are some of the typical special cases that you should plan for, but there may be other special cases that you need to consider too. For example, what would happen if we used negative numbers?
sumOddValues([1, 2, 3, 4, 5, -13]) // => still 9
Do you want the function to include negative numbers in the sum, throw an error, or ignore them like it is currently doing? It might be better to rename the function to sumPositiveOddNumbers so that future maintainers know that ignoring negative numbers was intentional. Writing a test case to document your decision is important.
It’s not a bug. It’s a feature.
Problem #3: This function does not work correctly for a simple and valid situation, even though it should be able to handle it. We should not worry about more complicated cases that are not common.
sumOddValues([2, 1, 3, 4, 5]) // => 11
The code was not written correctly because it included the first even number in the sum when it should not have. To fix this, the code should have included a second argument to be used as the initial value for the accumulator. This problem could have been avoided if the code had been tested with different cases, such as all-even numbers, a list that has 0 in it, and an empty list. This is a sign of inexperienced code.
14. Not Questioning Existing Code
If you are not an experienced coder who works alone, you will likely come across some bad code in your career. Beginners may not recognize it and may think it is good code because it works and has been in the codebase for a long time. Worse, if the bad code uses bad practices, the beginner may be tempted to use the same bad practices elsewhere in the codebase.
Sometimes code looks bad but it may have a special condition that caused the developer to write it that way. In this case, the code should have a detailed comment explaining the condition and why the code was written that way.
15. Thinking too much about the most effective way to do something
Stop believing that there is a single best practice for any given situation. Instead, focus on what you can do best and make educated decisions. Do not take advice at face value, question everything and challenge all theories. Invest time to find better practices and do not be afraid to make mistakes.
16. Thinking too much about performance
Premature optimization is the root of all evil (or at least most of it) in programming — Donald Knuth (1974)
Donald Knuth said that if you cannot measure the performance of your code, you should not try to optimize it. This is still true today, even though programming has changed a lot since then.
There are some obvious optimizations that you should always consider, such as not flooding the event loop or blocking the call stack in Node.js.
However, any non-obvious optimization that is done without measuring the performance first is not recommended, as it could lead to unexpected bugs. Therefore, it is best to not waste time optimizing unmeasured performance problems.
17. Not Targeting the End-user Experience
When adding a feature to an application, think about how it fits into the current user interface and how it will be used by the end user. If the feature requires input from the user, add it to the existing form. If the feature is a link, add it to the existing menu of links. Put yourself in the shoes of the end user and think about how to make the feature easy to find and use, not just how to make it exist in the application.
18. Not Picking the Right Tool for the Job
It is important to choose the right tool for the job. Just because a tool is popular or you like it, doesn't mean it is the best tool for the job. You should familiarize yourself with the different tools available and be open to learning new ones. Even if you are comfortable with the tools you already know, it is important to keep up with the new tools that are available, as they can help you do the job better and faster.
19. If we don't understand the code, it will lead to issues with the data
Managing data is an important part of a program. The program is used to add new records, delete old ones, and modify existing ones. If the code of the program has any bugs, it can cause the data it manages to be in an unpredictable state. Beginners may not realize the connection between code and data, and may not think it is important to fix the bugs.
However, if the bugs are not fixed, it can lead to data integrity problems that can become unrecoverable. To protect yourself from these problems, use multiple layers of data integrity validations, such as front-end, back-end, network communications, and databases. If that is not an option, use database-level constraints. Make sure to use all of the database constraints when adding columns and tables to the database.
- A NOT NULL constraint on a column means that the column cannot have any empty or null values. If your application requires a value for that field, it must be specified in the database.
- A UNIQUE constraint prevents a column from having duplicate values in the entire table. For example, this is useful for a username or email field in a Users table so that each user has a unique username or email.
- A CHECK constraint is a rule that must be followed for data to be accepted. For example, if you have a column that should only contain values between 0 and 100, you can use a check constraint to make sure that only values within that range are accepted.
- A PRIMARY KEY is a column in a table that has unique values and cannot be empty. Every table in a database should have a primary key to identify its records.
- A FOREIGN KEY constraint requires that the values in a column must match the values in a column of another table, usually the primary key of that table.
If multiple changes need to be made to the same data source and they rely on each other, they must be grouped together in a transaction that can be reversed if one of the changes fails.
20. Starting something from scratch instead of using existing methods or ideas
Sometimes it can be difficult to decide which tool to use for a specific task in programming. It is important to do research and try out different options before making a decision. If the task is something that has already been done before, it is usually best to use the existing tool instead of reinventing the wheel.
However, if the task requires something unique, it may be necessary to rethink the design of the wheel. Open-source tools are often a good choice because they can be debugged and replaced easily. It is also important to be mindful of not using too many tools or libraries for a task, as this can be inefficient.
21. Having a negative attitude towards code reviews
If you are new to coding, it is important to view code reviews as a learning opportunity rather than criticism. Don't be afraid of code reviews, welcome them and appreciate them. Learn from them and thank your reviewers when they teach you something.
Remember that you are always learning and code reviews can be a great resource. Even if the reviewer is wrong, you can use it as an opportunity to teach them something. Teaching is a rewarding activity for a programmer.
22. Not keeping track of changes made to a project or code
Newbies sometimes underestimate the power of a good source/revision control system, and by good I mean Git. Source control is not just about pushing changes for others to have and build on, it is about having a clear history of the progress of the code.
This is why it is important to commit often and early, and to use present tense verbs in the commit subject line. It is also important to be detailed in the commit messages, but to keep them short. Source control is also about discoverability, as it can help you identify what code introduced a bug into the program.
Additionally, Git offers many features that can be used to improve the coding flow, such as staging changes, patching selectively, resetting, stashing, amending, applying, diffing, reversing and many others. The more of these features you know, the less of a newbie you are.
23. Using too much of a shared resource or piece of information
I'm saying that shared state can cause a lot of problems and should be avoided if possible. If it can't be avoided, it should be kept to a minimum. Every variable we define is a shared state that can be changed by anything in the same scope.
We should try to keep new states contained in small scopes and not let them leak out. Race conditions can happen when multiple resources try to change the same state at the same time. Don't try to use a timer as a workaround for this problem, and watch out for it in code reviews.
24. Having a negative outlook or outlook of defeat when making mistakes
Errors are a sign that you are making progress, and experienced programmers appreciate them. If you are new to programming and find errors intimidating, you should try to look at them as helpful tools. Some errors should be upgraded to exceptions, which are errors that you plan for, while others should be left alone and cause the application to crash.
25. Technology is always changing. Don't limit yourself, always be open to learning new ones.
I used to limit myself to only doing front end development, but now I'm a full stack JavaScript developer. I'm coding with node, react, express and working with different databases. I'm telling everyone not to make the same mistake I did and to stay open minded and keep learning so they can do well in the industry.
26. Not taking any breaks
You need to take breaks when you are working, both for your brain and your body. It is easy to get so focused on your work that you forget to take breaks, but this is something you should not ignore. Make sure to include breaks in your workflow and take lots of short breaks. Get up from your chair and take a short walk, and use this time to think about what you need to do next. When you come back to your work, you will have a fresh perspective.
You have read a lot, so you deserve a break.
Mình hy vọng bạn thích bài viết này và học thêm được điều gì đó mới.
Donate mình một ly cafe hoặc 1 cây bút bi để mình có thêm động lực cho ra nhiều bài viết hay và chất lượng hơn trong tương lai nhé. À mà nếu bạn có bất kỳ câu hỏi nào thì đừng ngại comment hoặc liên hệ mình qua: Zalo - 0374226770 hoặc Facebook. Mình xin cảm ơn.
Momo: NGUYỄN ANH TUẤN - 0374226770
TPBank: NGUYỄN ANH TUẤN - 0374226770 (hoặc 01681423001)
All rights reserved