Add comments to explain ideas.

Some things are not obvious from self-documenting code.

Comments should explain why choices were made. They should talk about the history of the code and what led to the current approach. Comments should cover ideas not shown in the current source code. Source code by itself only reveals so much.

The variable and method names should make it obvious what the code is doing. We don’t need comments to explain what the code does. That is the code’s job. Comments should explain why the code does what it does.

This is much easier if a method does only one thing. If you see the word “and” in a method name, that’s a hint it may be doing too much.

Don’t be afraid to refactor.

There’s no time like the present to make things better.

Refactoring should be a regular part of your development habit. Write tests, develop the code, and refactor to make it better. Then the cycle repeats! Over and over, you add more tests, add more code, and refactor to make the whole thing better. Always refactor, every day.

Each time you refactor, you have a chance to make the code just a bit more awesome. Simplify. Make it more DRY. Apply the SOLID principles. Improve the code. Make it more clean.

Software engineering a wonderful profession. Unlike physical engineering, we can replace components and make improvements pretty easily. If you build a bad bridge, it becomes a year-long project to replace it. If you have a bad subroutine, it can be fixed in under a day.

So get to it.

Sometimes, disaster strikes.

Sooner later, something horrible and unexpected happens.

You can’t prepare for everything.

Oh, don’t get me wrong. You should certainly try. Examine every component of the system and imagine what would happen if it fails. It’s not enough to simply run the database in a cluster. Sometimes a process will fail part way through, leaving a system in an uncertain state. This can be a deplorable and embarrassing situation that should be avoided as much as possible.

Disasters will occur sooner or later. You will be remembered on how well you were prepared.

Assume every component in the system will have a failure at some point. Disk drives will fail. Network connections will drop. There will be a flood. A fire. A backhoe will cut through the fiber line. A hurricane rips the roof off the datacenter. Criminals steal the servers. Hackers break into the network. Terrorists destroy a building. An open window in winter causes a water pipe to freeze and break, flooding the floor and the eight floors below it.

All of these things have happened and can happen again. Or it could be something completely new. It pays to be prepared.

Avoid manual labor.

If your job involves telling computers how to do things automatically, then you should not do things manually.

Someday, the robots will take over and do all of the manual work. Already, robots assemble cars, plow the fields, pick items in a warehouse, vacuum the floor, choose songs for your mixtapes, find books you’ll like, fly planes, park cars, and perform surgery. In the future, all kinds of manual work will be done by robots.

Our job as developers is to write the programs that control these robots. We are the puppet masters. Those robots do what we tell them to do.

So why in the world would we do manual work ourselves?

When you find yourself doing something manually for the third time, you should create a program or shell script to perform that function for you. Share your scripts with the team.

Treat these scripts as software. Check them into version control. Give them code reviews. Replace them with more sophisticated programs if the script gets more use or requires some complexity.

When it’s your job to automate tasks that people used to do manually, it’s really absurd when you do things manually. Don’t be that guy.

Find the real requirements.

Most projects suffer delays and rework because the true requirements are rarely fully understood.

Business analysis is harder than it looks. While the product people have an idea in their heads about what they want, turning that into a complete functional description of the finished application is a major undertaking. The difference between an idea in someone’s head and the final, complete description is a long, long road.

Doing the analysis can be like mining for gold. The real requirements are buried deep beneath the surface. You have to keep digging to get at them. Unfortunately, if you fail to do the hard work, you will end up with insufficient and wrong requirements. Then a lot of development time will be wasted building things that will need to be re-done later. Time spent constructing features not desired is one of the saddest forms of waste in an engineering team.

Beware of assumptions, misconceptions, and politics. It is very easy to make assumptions during the development process. The best way to defeat bad assumptions is through repeated communication, documentation, and redundancy. Review the requirements from time to time, to make sure everyone understands them correctly. This also helps with misconceptions. The more times people review things, the more opportunities to uncover gaps in understanding.

Politics are another monster entirely. People who are more concerned about their own standing in an organization than with the success of the team sometimes don’t ask in the team’s best interest. Always be wary of political animals. They’re dangerous.

Another thing to keep in mind: you can’t estimate work effort until you know what you’re really supposed to build. Anytime you’re asked to provide estimates without doing a proper analysis, you’re going to have a bad day.

Or year.

Change is hard.

You can’t force people to change.

Changing behavior is extremely difficult. Habits burn pathways in the brain, making it very easy to continue the habit without actually thinking about the mechanical processes involved.

When you see someone else exhibiting bad behaviors, it can be very challenging to convince them to change. One can’t simply demand a change and expect a satisfactory result to occur instantly. People are complicated.

New habits take time and practice. There needs to be a strong motivation to replace an old habit with a new one. Then the person needs to practice the new habit every day for a period of weeks until the new habit has fully replaced the old one. Because this requires a lot of conscious effort, and the old habit does not, people very often fall back into their old habits.

While you can’t force a person to make this change, you can show them a better way of doing things and help them with the new habits. The more positive reinforcement there is of the new habit, the more likely it will be able to stick.

Simplify the constructor.

Sometimes its dependencies can be moved to other components

For example, if dependencies are only used by a validator, consider moving those dependencies from the constructor to the validator. After all, they are really the concern of the validator.

This will simplify the constructor. Anytime you have a good opportunity to simplify code and separate concerns, you should take it and make the improvements. This will make your software easier to maintain and enhance.

Avoid string concatenation.

There are clearer ways of organizing strings.

Some people like to assemble strings through a series string concatenation commands. This can get rather complex. Building an interesting string this way can well over a dozen lines of code. When a new person has to review the code, it can be hard to tell at a glance what the author’s intent is.

It’s a lot easier to read sprintf statements. With one simple statement, a complex string is easily assembled. Also, the sprintf statement separates the format of the message from the data.

Some people object to the use of sprintf because of outdated notions of a performance penalty from bringing in a standard I/O library. In reality, the benefits of having clear code almost always outweigh these trivial differences in performance.

Use the AAA pattern in tests.

Arrange, act, assert.

These are the key three components of good tests. First, one arranges a situation. This sets up the initial conditions of the test, modeling what the existing values of variables should be. In more complex tests, this can involve several factories or data providers. External dependencies may be mocked out, as those are typically tested elsewhere.

The second section is to act. Here, one performs the action that needs to be tested.

Finally in the third section, the test will assert that the final conditions are expected.

It is helpful to label these sections with “given”, “when”, and “then”. This pattern models the acceptance criteria of a story. In this way, one can use the acceptance criteria as a driver on how to drive the main goals of the test.

Bring a solution to the table.

If you’re not providing options, then you’re a part of the problem.

In any organization, there is typically that one person who is always negative about everything. They can always come with many reasons not to do things. They point out all of the problems, weaknesses, and defects in a project.

Because many of these are often legitimate concerns, these people do have some value. Problems cannot be ignored; they won’t go away by themselves. As we are all very concerned about quality and security, it benefits us to be aware of these issues and work towards remediating them.

Ah, but how?

When a person points out faults but does not provide any solutions, they are creating work for other people. It is much more useful to point out solutions and then also provide a variety of options on how to solve the problem. People who provide solutions are always considered to be more valuable than those who only uncover problems.

So be the person with the solutions. Do the extra legwork.