1) Defensive programming
The main purposes of defensive programming is to ensure that a piece of code works under unforeseen circumstances
, to improve comprehension and predictability and to simplify maintenance.
To program defensively, make sure each method (as well as class, variable and property where applicable):
* Has a clear and single purpose
* Has a clear name
with a clear intent (that matches the implementation)
* Does not have any unexpected side effects (i.e. it should not perform operations that is not to be expected)
* Is short and concise
* Is testable
* Validates input
* Handles exceptions
* Has a clear contract
* Has sensible return values
* Does not have any leaky abstractions (internal details and limitations should be kept internal)
* Use comments (with moderation) to explain why
things are done (if you feel that you need to explain how
something is done you should consider refactor the code instead)
Also, make sure that your code follows best practice guidelines, methodologies and programming techniques so that other programmers (and yourself after some time) easily can grasp how the code works.
2) Unit testing
Unit testing will reduce the number of bugs in production
, make it less risky to change and refactor code and will prevent any regressions to reoccur. When done right, it can also make you write better code. Sure, it can be tedious work and it takes time to write but it is totally worth it.
Read more about unit testing here:http://www.codeaddiction.net/articles/23/introduction-to-unit-testing-in-c
3) Adhere to the SOLID-principles
S.O.L.I.D. is an acronym that states five basic principles of object-oriented programming and design. These principles, when combined together, make it easy for a programmer to develop software that are easy to maintain and extend
. They also make it easy for developers to refactor code and avoid code smell.
S.O.L.I.D stands for (very simply put):Single-responsibility principle
Every class should have a single responsibility over a single part of the functionality provided by the software, and that responsibility should be entirely encapsulated by the class. If a class has more than one "reason to change" (i.e. it tries to solve many problems) it should be split up.Open-closed principle
It should be possible to extend the functionality of a class (open), but not to modify the class itself (closed).
Here's an example of what the "closed-part" means: A method (MakeJuice) is taking objects as parameters that implements a specific interface (IFruit). When done right, we should be able to create new types of fruit and run it though MakeJuice without needing to change the implementation of the method, this means that it is "closed".Liskov substitution principle
Objects in a program should be replaceable with instances of their sub-types without altering the correctness of that program. Interface segregation principle
Split interfaces which are very large into smaller and more specific
ones so that any implementation only will have to know about the methods that are
of interest to them.Dependency Inversion Principle
If a class X depends on a another class Y, make sure that the dependency is done through an interface. This means that you can replace Y with another implementation without having to change X.
4) Dependency injection / Inversion of control
Inversion of Control (IoC) is a pattern where you let a framework provide dependency instances for classes. Dependency Injection is a sub-type of IoC where the dependencies are fed to the class through the constructor.
DI / IoC has several benefits:
* It decouples the codes, which in turn increases reusability
, makes it easier to test and lets you manage the instantiation of objects on a higher level
* It focuses a module on the task it is designed for
* It prevents side effects when replacing a module
* It makes the code testable
* It promotes good OOP design
Read more about DI / IoC here:http://www.codeaddiction.net/articles/10/dependency-injection-in-c---a-simple-introduction
5) Make your objects as immutable as possible
The last practice is something that has been borrowed from functional programming - the idea that you never should be able to change an object. If an object needs to be changed, it should instead be recreated.
There are several benefits:
* The objects are thread safe
* It prevents objects having invalid states
* Letting external code change the state of your objects makes the code hard to read and grasp
* Immutability makes it easier to write, use and reason about the code
* Immutability makes it easier to parallelize your program as there are no conflicts among objects
* References to immutable objects can be cached as they are not going to change
Now this is a principle that might be hard to live by, but you can use the following as a rule of thumb: Classes should be immutable unless there's a very good reason to make them mutable.... If a class cannot be made immutable, limit its mutability as much as possible.
What is a practice that you think would make ones code better? Add a comment below!