1. After you modify the code, test yourself

“Modify the code, test yourself” is a basic quality every programmer must have. In particular, don’t have the “I just changed a variable or I just changed a line of configuration code, don’t test yourself” mentality. After changing the code, try to ask yourself to test it, you can avoid a lot of unnecessary bugs.

2. Test all methods as far as possible

Input verification is also a basic quality that every programmer must have. Your method handles, “must validate parameters first”. For example, whether the input parameter is allowed to be empty, and whether the length of the input parameter matches your expectation. Make this a habit. Many “low-level bugs” are caused by “not checking parameters”. If your database field is set to vARCHar (16), the other party sends a 32-bit string, you do not check the parameter, “insert database direct exception”.

3. Consider interface compatibility when modifying an old interface

Many bugs are caused by “incompatible” modifications to old interfaces. The key this problem is more serious, may directly lead to the failure of the system. This is a common mistake for novice programmers. If a new function reuses an old interface, try to inherit and override the method. When modifying the old interface, try to create a new method instead of directly modifying the old interface. The code is as follows:

/ / the old interface
void oldService(A,B); {// Compatible with new interface, pass null instead of C
  newService(A,B,null);
}
// New interface, can not delete the old interface, need to do compatibility.
void newService(A,B,C);
Copy the code

4. Add comments for complex or special processing

When writing code, there is no need to write too many comments, good method variable names are the best comments. However, if it is “code with complex business logic”, it is really necessary to write “clear comments”. Clear comments, more conducive to later maintenance.

5. Shut down the I/O flow

We should all have such experience, Windows system desktop if “open too many files” or system software, will feel that the computer is very jammed. Of course, our Linux server is the same, usually operate files, or database connection, if the IO resource flow is not closed, then the IO resource will be occupied by it, so that others can not use it, which causes “resource waste”. After using the IO stream, you can use finally to close it

FileInputStream fdIn = null;
try {
    fdIn = new FileInputStream(new File("/jay.txt"));
} catch (FileNotFoundException e) {
    log.error(e);
} catch (IOException e) {
    log.error(e);
}finally {
    try {
        if(fdIn ! =null) { fdIn.close(); }}catch(IOException e) { log.error(e); }}Copy the code

After JDK 7, there is a more elegant way to close streams, “try-with-resource”.

/* * Pay attention to the public account, a little boy picking up snails */
try (FileInputStream inputStream = new FileInputStream(new File("jay.txt")) {
    // use resources   
} catch (FileNotFoundException e) {
    log.error(e);
} catch (IOException e) {
    log.error(e);
}
Copy the code

6. Code takes steps to avoid runtime errors (e.g., groups of boundaries overflow, divided by zero)

In daily development, we need to take measures to avoid runtime errors such as “array boundary overflow, division by zero, null pointer”. Similar code is common:

String name = list.get(1).getName(); // List may be out of bounds because it does not have to have 2 elements
Copy the code

Therefore, we should take measures to prevent array boundary overflow. Example:

if(CollectionsUtil.isNotEmpty(list)&& list.size()>1){
  String name = list.get(1).getName(); 
}
Copy the code

7. Try not to make remote calls or database operations in the loop, and prioritize batch operations.

Remote operation or database operation is “more consumption of network, IO resources”, so try not to loop in the remote call, not in the loop operation database, can “batch one-time check back as far as possible do not cycle many times to check”. (But don’t look at too much data at once, do it 500 times.)

remoteBatchQuery(param);
Copy the code

Example:

for(int i=0; i<n; i++){ remoteSingleQuery(param) }Copy the code

8. After writing the code, imagine how multi-threaded execution would work, and pay attention to concurrency consistency

We often see some business scenarios, first check whether there are records, and then perform corresponding operations (such as modify). However, this is not an atomic operation. Imagine multi-threading, and you’ll find that there is a problem.

For example:

if(isAvailable(ticketId){ 
    1, increase operations to cash2, deleteTicketById (ticketId)}else{ 
    return "No cash coupons available";
}
Copy the code

Thread A adds cash; thread B adds cash; thread A removes ticket flag 4. Thread B deletes the ticket flag, which obviously has a “concurrency problem” and should “take advantage of the atomicity of the database delete operation”, as follows:

if(deleteAvailableTicketById(ticketId) == 1) {1, add operations to cash}else{ 
    return"No cash coupons available"}Copy the code

Therefore, it is also necessary to have the habit of “after writing the code, think for yourself whether there is a concurrency consistency problem with multi-threaded execution”.

9. Obtain the attributes of the object and check whether the object is empty

This point also belongs to the “take measures to avoid runtime exceptions”, but I still take it out, as a key to write, because usually null pointer exceptions are too common, a hand tremor is not noticed, resulting in the null pointer to report to the production environment. So, when you want to get the properties of an object, try not to believe that “theoretically not empty”, we will get into the habit of determining whether it is empty, and then get the properties of the object. Is:

if(object! =null){
   String name = object.getName();
}
Copy the code

10. Multithreaded asynchrony prioritizes the appropriate thread pool over new threads, considering whether the thread pool is isolated

Why thread pools in preference? There are several advantages to using a thread pool. It helps us manage threads and avoid the added resource consumption of creating and destroying threads. Improve response speed. Reuse. At the same time, try not to share a thread pool for all businesses, and consider “thread pool isolation.” Allocate different thread pools for different critical services, and then consider the appropriate thread pool parameters.

11 manual code business SQL, take the first database run, but also explain to see the execution plan.

Manually write business code SQL, you can take it to the database to run, see if there are syntax errors. Some friends bad habit is to write the code package to the test server, in fact, SQL into the database to execute, can avoid a lot of errors.

At the same time, also use “explain to see your Sql execution plan”, especially if the index is not used.

explain select * from user where userid =10086 or age =18;
Copy the code

12. When invoking third-party interfaces, consider exception handling, security, and timeout retry.

Calls to third-party services, or distributed remote services, need to be considered

  • Exception handling (e.g., if you call someone else’s interface, what to do if there is an exception, retry or fail)
  • Timeout (it is impossible to predict how long the other interface usually returns, generally set a timeout disconnection time, to protect your interface)
  • Number of retries (Do you need to try again if your interface fails? Consider this problem from a business perspective)

13. Interfaces need to be idempotent

Interfaces need to consider idempotent, especially grab red packets, transfer these important interfaces. The most intuitive business scenario is “the user clicks twice in a row”, whether your interface holds.

  • Idempotent (idempotence) is a mathematical and computer concept commonly found in abstract algebra.
  • In programming, an idempotent operation is characterized by any number of executions having the same effect as a single execution. An idempotent function, or idempotent method, is a function that can be executed repeatedly with the same parameters and achieve the same results.

Generally, idempotent technical solutions are as follows:

  • Query operation
  • The only index
  • Token mechanism to prevent duplicate submission
  • Delete Deletes the database
  • Optimistic locking
  • Pessimistic locking
  • Redis and ZooKeeper distributed lock (Redis distributed lock was used to grab red packets in the past)
  • State machines are idempotent

Consider linear security in multi-threaded cases

In the case of “high concurrency,” a HashMap can loop in an infinite fashion. Because it is nonlinear and secure, consider using ConcurrentHashMap. So try to get into the habit of doing this as well. Don’t backhand a new HashMap();

  • Hashmap, Arraylist, LinkedList, TreeMap, etc., are linearly insecure;
  • Vector, Hashtable, ConcurrentHashMap, and so on are linearly safe

15. Consider master-slave delay

The code logic of insert and then query is common and can be problematic. The general database is to have the master library, the slave library. Write to the master library, read to the slave library. If a master-slave delay occurs, it is very likely that you inserted successfully, but could not query it.

  • If it is an important business, you need to consider whether to force the master library to read or modify the design.
  • However, there are some business scenarios where it is acceptable to have a little bit of master/slave delay, but it is a good habit to have.
  • After writing the code to operate the database, think about whether there is a master-slave delay problem.

16. When using cache, consider cache consistency with DB, and (cache penetration, cache avalanche, and cache breakdown)

In layman’s terms, we use caching for “fast lookup and low interface time.” However, when it comes to caching, you need to “pay attention to the cache and database consistency”. At the same time, cache penetration, cache avalanche and cache breakdown need to be avoided.

  • Cache avalanche: A large amount of data in the cache reaches the expiration date, but a large amount of query data causes the database to become overloaded or even go down.
  • Cache penetration: Data that does not exist must be queried. If the cache does not hit, the data must be queried from the database. If no data is found, the data will not be written into the cache.
  • Cache breakdown: When a hot key expires at a certain point in time, a large number of concurrent requests are sent to the key, resulting in a large number of requests to the DB.