Business words make better names.

Use domain-specific types.

When working with business concepts, it is best to create customized types and objects that reflect the business domain.

For example, a social security number is not really a number. It’s a common rookie mistake to represent SSN with an integer; if you strip out the dashes and concatenate the digits, it looks like a number. But an integer does not have to be nine digits. An integer does not have leading zeroes for left padding. No, a social security number is a string of nine numeric digits. Because it has specific rules regarding what can and can’t be a legitimate SSN, it should be its own type. As an object, it can also have a toString method that puts the dashes back in.

Similarly, a ZIP code is likewise not an integer. The specification expects it to be a string of five numeric digits with zeroes left padded. Optionally, there may be a ZIP+4 extension. Don’t use an integer for this; create a custom type.

Type checking and other validations on the object can prevent errors. Using an object, you can control the format, the bounds, and the content. Within the program, other developers will have an easier time recognizing and working with domain type objects when they can see the rules listed in the object’s class. When people use primitive types, other developers are left to guess what the valid values could be.

Learn the adapter pattern.

You’re going to need an adapter.

As you use more abstract classes and interfaces, you should start to use the adapter pattern.

Then, when an API or interface needs to change, you only have to update the adapter.

Imagine you’re taking orders from various partners, each of which sends a message written in a proprietary format. If you have a standard object passing orders into your internal system, you can separate the conversion of each proprietary format into separate adapters.

Each adapter would have one job: convert the message from the foreign language into the internal format. Then, if there are any changes in your partner’s messaging protocols, a developer can make the changes in the adapter.

Don’t make bugs to hide bugs.

Watch out for bugs that have legacy workarounds.

It’s bad enough when an application has bugs. Occasionally, though, you find a situation where a developer coded a workaround for a bug into the application. Perhaps the developer could not figure out how to fix the original bug. Maybe adding the workaround was the only thing he could think of to get the system working again.

Unfortunately, now the system has two bugs, the original one and the workaround. Now, if someone removes the workaround, the first bug emerges once more. On the other hand, if another developer finds and fixes the original bug, the workaround will cause the system to malfunction. To thoroughly repair the system, both will have to be removed, which may cause other effects.

These kinds of bug pairs can be very nasty and hard to find.

Do your best to avoid this situation. Don’t write workarounds. Don’t give up when trying to find the original bug. Most of all, keep everything covered in healthy tests and ensure that the tests run red first. Workarounds often hide behind weak tests.

Use fat models.

Move your business logic down to your models.

Fat models are preferable to fat controllers. As part of the Model-View-Controller pattern, the View handles the user interface, the Controller routes traffic, and the Model contains the business objects. In the interests of keeping controllers simple, one should move business logic out of the controllers and into the models. So if you have to choose between a fat model and a fat controller, go for the fat model. Here, “fat” refers to the size of the class.

However, models don’t have to be large. In fact, it is preferable to have several thin models instead of one large model. By moving separate concerns to other classes, you can reduce the size of the models. Models can call other models.

Not all models need to be data access objects. Sometimes models only contain logic or work with intermediate objects that aren’t persisted in a database.

Some people prefer to keep the data access in a repository class, separate from the business logic, which slims down the models even more.

Ultimately, as things get separated out, the fat leaves the system.

Use thin controllers.

Some guidelines for the MVC technique.

MVC stands for Model-View-Controller, a popular programming pattern used in web applications. The Models represent the business objects, which contain business logic and database access. The Views handle the presentation of data to the user, the user interface. Finally, the Controllers route traffic from the business objects to the user interface.

As a good practice, you want thin controllers. Having too much logic in a controller is like having too many methods in a class. Each class should have one job. To reduce a controller’s responsibilities, one should move logic to other classes.

Controllers should not format output; instead, the Views should handle this. You should move data validations, user authorization/authentication, and other functions to helper classes. Models can manage business logic, data storage, and complex queries.

Controllers can call other controllers, which is very helpful in reducing the responsibilities of one controller. Similarly, a controller can route data from multiple models.

What’s left are thin controllers that are easily understood and also very simple to maintain.

Take engineering seriously.

How things are built is important.

Compare software engineering to the architecture and construction of buildings.

Would you step into a building that was poorly designed, not thoroughly tested, or rushed in construction “to be repaired later”? It probably wouldn’t be your first choice.

When you set foot in a building, you are making a lot of assumptions. You assume that a competent architect designed the plans for the building. You assume that the construction workers who assembled it followed the best practices of the day. You assume that parts used in construction were of sufficient quality for the task.

After all, if buildings tended to collapse on people, there would be a lot of outrage, and people would be held accountable.

In software engineering, there aren’t many government-mandated regulations around the construction of software. Developers often use libraries and tools without fully understanding their origin or quality. And many companies hire developers that have poor programming skills and limited knowledge of best practices. As a result, many software systems are full of defects.

When you perform software engineering, think of the architects and construction of massive buildings. It would be best if you endeavored to have the same level of quality.

Automate repetitive tasks.

Robots are your friends.

We used to plow the fields manually with a hoe. Then we tied the hoe to an ox, and things went faster. As people developed engines, the oxen became dinner, and people switched to tractors. Today, we have robots driving the tractors.

More and more, we replace tedious manual work with automated robots. The robots never complain, and they can be more reliable. You should apply the same idea to your own work. Why should you perform many mindless, repetitive tasks? Instead, you can automate them away with some scripts and programs.

If you find yourself doing the same thing three times, you should automate it with a script. Save yourself the manual work. The chances are good that if you’ve done something three times, you’ll probably have to do it dozens of times in the future. It’s worth the effort to bring in the robots.

This rule applies to practically anything:

  • builds
  • tests
  • document generation
  • deployment
  • reports
  • queries
  • health checks
  • prep work and setup
  • investigation

So quit working so hard and make more robots.

Don’t log personally identifiable information (PII).

It pays to be more secure.

Don’t commit code that logs PII. You don’t need this kind of information in your logs. Plus, the number of people who have access to logs can be rather large. The best practice is to restrict access to information to only those required to be able to view it. Logs serve many purposes, from analyzing performance to auditing access to tracking down system problems and more. In most of these use cases, people don’t need access to PII. To better protect your customers, the system should never send this information to a log.

It is safer to do your log analysis using system IDs instead. After all, from a performance point of view, the customer’s PII is not relevant. You can check out the individual user tracks by examining IDs.

Thus, as part of the code review, developers should carefully examine which data the program sends to a log file.

PII should only be stored, encrypted, in the database. Ideally, it would be best if you segregated tables with PII from other application tables. Keeping the number of tables with PII to a minimum reduces risk, especially as the application grows in size and complexity. You can also reduce the number of people with access to those tables when they are kept separate.

Pair programming has many benefits.

Two minds are better than one.

Working with a partner when you are writing code has many benefits. While pairing is not for everyone, many people have found pair programming to be beneficial for many reasons.

You can learn new techniques. No one knows everything, and it always helps to learn new things from others. At the same time, you can be teaching new techniques to someone else. These methods and skills can range from simple things like keyboard shortcuts and other development tools to mastering more complex patterns.

Having two sets of eyes on the code also helps to catch minor errors faster. A typo may be syntactically correct but still cause a bug in the program. The two of you can prevent lots of little problems this way.

Pairing is also great for sharing institutional knowledge. Whether it’s the intricacies of the build system or the complexities of the business domain, having a continuous conversation while programming can help spread the wisdom.

Development speed is also faster when pairing. There are fewer distractions, and the focus is a lot higher. Furthermore, TDD is easier when pairing, and when used together, make for a powerful combination.

So while it’s easy enough to come up with many reasons not to pair, one shouldn’t overlook the many reasons to team up and get stuff done.

Practice the SOLID principles for cleaner code.

Write software that is loosely couple with high cohesion.

When you follow the SOLID principles (single responsibility principle, open/closed principle, Liskov substitution principle, interface segregation principle, and dependency inversion principle), your software tends to have smaller classes that work together more easily.

The classes will become more loosely coupled so that a change in one does not always require a change in another. The classes will also have high cohesion, meaning that all of its methods are closely related to the class’s primary responsibility.

When you arrange classes in this manner, you create systems that are easier to maintain and extend over time, which will reduce work for yourself and future developers as new requirements and feature requests come in.

These habits will also remove code smells, reduce complexity, and make the code easier to read. These techniques may be hard to practice at first, but over time you will get better, and the quality of your software will improve dramatically.