Everything should have a code review.

Don’t let it be a rubber stamp.

Code reviews correct mistakes. A second (or third) set of eyes looking over your code may catch some areas of concern you missed. These reviews help prevent defective code from being promoted forward, saving you rework later.

They also provide an excellent way to share knowledge. The developers doing the review can learn from you and the code you wrote. They learn more about the part of the business your code covers. They learn the technical details of the new features and how you have implemented them under the covers. They learn patterns and best practices that you put forth. Observing these things inspires them to write code better.

Code reviews also provide a friendly environment for constructive criticism, which gives you an excellent way to get advice from others on improving your techniques.

Finally, code reviews make coding fun. Through collaboration, we all get better because our friends make us better. This cooperation makes us all more awesome. And who doesn’t want to be more awesome?

Also try: pairing.

Blame the SQL.

Performance problems are often caused by poorly formed SQL statements.

Learning to use SQL properly takes practice. Unfortunately, people who do not fully understand SQL often create queries that are much slower than they need to be. Unfortunately, with the power of SQL, the speed difference between an optimized query and a poorly written query can be several orders of magnitude. In extreme cases, an inefficient query might take minutes, hours, or days to complete.

Because database queries result in a high number of I/O operations, both logical and physical, they can be the most time-consuming part of an application’s operation. If you think about it, you have a CPU that can perform over a billion operations a second, memory that operates at GHz speeds, and a bus that tosses over a GB of data around each second. And then you have a disk drive that is physically spinning around and moving a robotic arm to control magnetic fields. Compared to what the rest of the computer can do, waiting on disk I/O is like driving a Maserati into a pool of molasses.

Because of this, developers must optimize their database queries. For example, if a query is 1000 times slower than it should be, users will suffer the pain. On the other hand, if your CPU calculates the addition of two numbers 1000 times slower than it should, it will still finish in less than a millisecond; you won’t notice.

So when you find your application is performing slowly, the problem is almost always related to poorly written SQL statements.

Sometimes this is a result of improperly using an ORM. The ORM is a powerful tool, but it is easy to do things wrong and end up with N+1 queries when you meant to have one query. It’s also easy to end up with table scans. The ORM hides the details of the SQL from the user and thus hides the performance problem. So that’s why it is often the culprit when problems exist.

So that’s where you should look first.

Examine method signatures in your libraries.

These may give you more options for the methods you use.

Your libraries and frameworks have many methods at your disposal, and while you are likely familiar with some of them, it is well worth your time to examine these methods closely. Sometimes they have additional optional parameters in their method signatures that may give you new abilities. As a result, it can be surprising to learn about the full functionality of your tools.

Also, while you’re there, look for other methods with which you might not be familiar. Many developers mistakenly implement functions that perform the work already available in the libraries they use. Instead, it would be best if you simply used the library’s implementation. Chances are good that the people who have written the library have received feedback from hundreds of users and thus have optimized it for the best use. Take advantage of the hard work of others.

Require a small number of parameters.

Your method definitions should not be overly long.

A method with a long series of parameters can become a problem for other developers to use. Every time they use the method, they’ll need to research its full definition, determine which parameters are nullable, and what order they need to be in the call. Fortunately, better languages have named arguments, so the order becomes less of a concern. Still, a large quantity of parameters causes headaches.

You can group associated parameters into an object. For example, you can use a Coordinates object instead of (x, y, z). Passing Coordinates instead of three unknown temporary variables is much easier to understand.

Other parameters can be gathered into objects when they are related. For example, a PaymentAccount object could include the account number, subaccount number, group number, company, and account type; this would replace five parameters with one.

Having too many dependencies is a sign your method is doing too much. Try to reduce your method’s responsibilities, and the number of required parameters should also come down.

Don’t start over from scratch.

Avoid this temptation.

When you come into a project with a lot of legacy code that has many problematic issues, it is tempting to throw in the towel and start over from scratch. After all, greenfield development allows you to use the latest versions of all the libraries and avoid all the legacy ickiness.

Many people forget that the existing code has been reviewed, tested, and run in production for a long time—several years in most cases. Your new code has not. As a result, your code will likely repeat many of the problems addressed and fixed long ago in the older code. Suffering through these problems all over again will be highly frustrating to the business.

Your new code will probably miss something important. It will probably not have all of the features the customers of the old code base enjoyed. This lack of functionality will lead to more headaches.

The better solution is to refactor the old code into components and services. Then, upgrade them separately.

Refactoring and improving are better than replacing and regretting.

Make time for analysis.

There must be time for analysis. Otherwise, there will not be enough time for development either.

If you do not have time to do the necessary analysis and write specifications, then you probably will not have enough time to develop the code.

In these cases, developers blindly stumble forth and waste time developing features based on guesses and assumptions. Hence, the functionality will not match what is desired by the business. As a result, the team writes a lot of code but doesn’t deliver working software. The code will require a lot of rework, which will take much more time.

Specifications are important. Without them, you will end up developing the wrong thing.

The development team must fully understand the business requirements; otherwise, the results will be suboptimal. Implementing the wrong thing is the most significant cause of delays in properly delivering working software. The best practice is to get it right the first time; you cannot do that if your analysis is incomplete.

Pair programming accelerates development.

Everything is awesome, everything is cool when you’re part of a team.

Programming with a friend has many advantages over programming alone. These include:

  • You automatically gain two experts on this section of the code. Having two experts avoids the problem where only one person knows how something works.
  • Problems are solved more quickly.
  • Integration is faster.
  • There is better adoption of standards and style. The automated style enforcement tools don’t catch everything, so it helps immensely to have two people working together to ensure that they keep writing software the right way.
  • You will handle interruptions better.
  • A teammate can bring another developer up to speed more quickly. This one-on-one training is particularly beneficial when a new person joins a team.

Don’t use primitives to represent domain ideas.

Use Value Objects instead.

An account number is not an integer. Neither is a phone number or a social security number. Similarly, zip codes are not integers. At first glance, they may look like integers, but they have additional rules and restrictions. For example, a zip code that begins with zero needs to print that zero; “07093” is a valid zip code, but “7093” is not.

In the United States, phone numbers should have ten digits. Plus, “555” cannot be the digits in positions four through six; such phone numbers are always fake, and they often appear in movies and television shows.

Interest rates are not floats. Banks typically require interest rates to be recorded to a specific number of significant digits. Also, there are often rules against having negative numbers for interest rates.

An amount of money is not a float. Most financial systems do not recognize any amount less than 0.01 dollars as valid. While some applications choose to represent money by using an integer to represent pennies, this is not a good choice either. You can always multiply two integers together, but you should never multiply two money amounts together. There is no such thing as a “dollars squared” unit of measurement.

Instead, use the Value Object pattern. Creating small classes representing these domain ideas gives you much more control over their behavior. It also limits the number of actions that can be applied to these objects, reducing the chances of invalid operations.

Relationships affect your code.

If you have poor relationships, the quality of your code will suffer.

How you deal with people outside your team will affect the code you write.

Better relationships and communication lead to better requirements and more clarity. If you don’t have a strong understanding of the requirements, there is no way your code will correctly deliver the desired results.

Our goal as software engineers is to deliver business value by writing code. However, it is critical to remember that people outside of the technology organization are the ones who measure this value. Never forget this.

Don’t be afraid to add more classes.

You won’t run out of paper.

Each class should have its own responsibility. For example, you should not have one class that calculates shipping, sends orders to the warehouse, and emails the customer. Instead, let the class have one job.

You won’t run out of disk space if you add more classes. Electrons are cheap.

Learning how to break up classes is an advanced skill. If you master this skill, your services will be in more demand.