Code is a language, and as such it has parts of speech. Classes and variables are nouns and should be named as persons, places, or things. Methods or functions are verbs and should take an action. Booleans, this is inclusive of classes, variables, methods, and functions, are predicates and should be named appropriately, i.e. IsGreen, AreValid.
After naming your code using the correct semantics start thinking about what it is used for. Names should have a purpose. That purpose is to communicate intent and should lend to the description of what your code is doing. In classic examples, if your method is intended to output a bark then the method should be named Bark. If a variable represents a users login attempt then the name might be userLoginAttempt.
Methods should be short
Keep your methods or functions to 6 lines or less. There are several reasons for wanting to do this. This most basic reason is that smaller methods are easier to understand. I don’t have to scroll to see a 6 line method in it’s entirety. If everything in the method is named correctly a 6 line method tells me, at a specific level of abstraction, exactly what it is doing. Small methods force us to extract and abstract. Abstraction is a fundamental tool in programming and allows us to encapsulate details behind a simple action. Methods that are longer can hide duplication and opportunities for reuse. A really large method, 20 lines or more, can hide an entire class that could be extracted. Lastly, A system composed of short well named methods can be easily debugged and quickly understood by new members on the team.
Organize your code
Within a single class, fields and properties go at the top followed by the constructor. Place public methods after the constructor. After the public methods it gets interesting. We don’t want references to go up in the file so I will place private methods that are called by the public methods right after all the public methods. Below the first order private methods place the second order private methods and so on until you run out of methods. This class level organization is subtle and can be a little time consuming, but it makes searching through a class for a particular piece of functionality much quicker. All of the class functionality defined up front followed by carefully and consistently diving down the levels of abstraction provides you with the appropriate level of detail when and where you want it.
Organizing your project or solution should be a bit different. Within the solution, projects should be grouped by feature and not by type. This is different from how I see most teams organizing their solutions and might be a hard sell for your team. The main reason they should be organized in this way is that it will make finding the correct piece of code to update or add to much easier. The ability to independently deploy your code is another reason. Separating by feature will make it easier to treat each feature as a plugin to the system.
Dependency injection, use it
Using dependency injection it is possible to turn features on or off, swap out functionality, and improve code quality with very little effort. Code quality is the first thing to improve when using dependency injection. Passing in the dependencies of an object either through constructor injection or property injection will make your code better. Dependency injection alleviates the need for each object to know all the requirements of the objects that it needs to use and you can reduce the duplication of object instantiation significantly.
Dependency injection offers the ability to swap out functionality by changing which objects are created to provide for a particular interface. For instance, in a drawing application, I might have the body of the application able to work with a shape. By injecting a different shape I can modify the specific behavior of the calls within the system. For this type of dependency injection I would most likely use a factory that was able to provide a listing of available objects and a mechanism for selecting which object to instantiate at runtime.
Similar to swapping out a set of functionality, you could simply turn that feature off. Providing back a disabled object instead of the functionally complete object. While this seems simpler, and probably is, it is also quite powerful. You can disable a feature on a running system simply by changing a text file. You can also use this strategy to work on a feature that is being deployed via a continuous delivery process. The feature would remain disabled until it was ready and then could be turned on by changing a setting.
There are several dependency injection frameworks that can help. You don’t really need one to get started though. Just create a class with a single method on it named Create. Create will instantiate and pass back a new instance of a class. Once you are comfortable using this simple form you can modify your factory to take options or even give you options at runtime explaining the objects that can be created.
If you didn’t write it don’t use it
Pretty simple rule, don’t depend on things you didn’t create. I know what you are thinking, what about the .Net framework or third party libraries? Well, you can use them. Of course you can use them. I am instead saying to not use them directly. Abstract the fact that you are using them. The basic process is to wrap anything you didn’t create in something you did create. Instead of using System.DataTime directly, create some kind of time keeper class that inherits from an interface and knows how to use System.DateTime. In your code depend on the interface and inject the time keeper at runtime using dependency injection. Only main should know about the third party libraries you are using. Your application code should be fairly ignorant.
I’m sure I sound like a broken record here. I feel like a broken record, but I can’t stress how much unit testing can improve your code. Unit testing provides instance regression tests over your system. In order to unit test you will have to abstract everything. Unit testing will also guide you down the path of dependency injection. Unit testing, especially test first or TDD, is the single most valuable coding practice I know.
Rules are meant to be broken
There are times when these rules don’t necessarily apply. All rules have an exception. I do implore you to follow these rules to the tee until you understand the benefit and can make an informed decision as to when to break the rule. Don’t simply break the rule because you feel like it. Have a good reason when you break one of these rules, a reason you can defend when questioned.
Please leave a comment below and join the discussion