New projects — do it right, now is your chance
We all start new projects. Some more often then others. But we all do. Some are big, some are small. It might be a hobby project that you work on alone. It might be a big project with tons of people. The importance of getting these things right from the start increases exponentially with the size of your project.
This is not a utopia. A project that generates zero warnings, passes all available static code analyzers and complies with standards and doesn’t break easily or uncontrollably is achievable. You just have to commit to it, and continue committing to it. Committing continuously to this requires discipline. Often this also requires strictness and enforcing strictness. Prevent yourself from doing stupid things, and prevent others from doing stupid things. Have systems in place that prevent anyone from doing stupid things. We are all human. We make mistakes. Don’t try to avoid mistakes, catch them and fix them before they have a serious impact.
As with everything in this world: take this with a grain of salt.
What works for me and what works for thousands of others might not work for you. Every project is different and has different requirements. Each business has different requirements. Some things might just not be affordable from a business point of view. Use your common sense.
Source version control
Make sure you have your infrastructure set up. Have an SCM server ready. The first line of code, documentation or whatever else should be nicely checked into a source version control system that is safely hosted on a server.
Your first action should not be setting up the Visual Studio project. No, it should be typing: git init.
In larger organizations it takes time before infrastructure is set up. It is not uncommon for it to take a couple of days before your IT department has set up an SCM server that you can all push your changes to.
There’s no excuse to not start using source version control right away. With decentralized source version control systems like Git and Mercurial you can start without even having a server around. You just work locally until the server is up and you can push. If you work with multiple people: use your own computer as a server that everyone pushes to until the real server comes online.
If you fail to do this, then it is inevitable that at some point you, or one of your team members will do something stupid like accidentally deleting something. Without an SCM system, all hope of recovering that will be lost. Working without an SCM system is a dangerous practice. In 10 years, maybe somebody very much needs to see how you got from nothing to the initial prototype. Not using an SCM system prevents anyone from figuring out what happened at the start of the project. It might not seem important to you now. However, time has shown again and again that this kind of information is exactly what you want when you are trying to figure out how that legacy project that you got shoved down your throat was designed.
For the sake of your future self and for the sake of the engineers who might have to maintain your work in the next decade: use an SCM system from the start.
Enable all warnings
This advice is language/technology agnostic. No matter what you use, your first step should be to enable all possible warnings you compiler/interpreter offers. If you can, configure it to report all warnings as errors.
Try enabling the highest warning level on a project that has done without for a couple of years. You’ll most likely get thousands of them. By that time it becomes hard to convince the stakeholders that you want to invest a week of your valuable time into “fixing warnings”. That’s hard to sell. You are not delivering any value to the company/product by fixing warnings for days on end. As an experiment, take your hourly rate, multiply it by the amount of hours required to fix all warnings and then ask yourself whether you’d pay that much money to fix warnings. You probably wouldn’t.
The point is that if you enable the highest warning level from the start, then each time the compiler/interpreter warns you, you simply fix it. Which typically requires a minute of your time. This way you won’t have to convince the stakeholders to spend days on fixing them later.
Aside from the fact that it will take a lot of time to fix all those small issues later, it can also help improve the stability and quality of your product. Compilers and interpreters emit warnings for a reason. Typically because they are potential pitfalls or clutter. You should not ignore them.
A warning about an unused variable is not useless. It tells you that it’s cluttering the code there. Nobody is using that variable. So why keep it around? Remove it. Don’t ignore warnings. Fix them. Now.
I am not just talking about warnings emitted by your compiler/interpreter. Also think about adding static analysis to your code base right from the start. Doing C++? Integrate CppCheck right from the start. Doing Python? Integrate PEP8 and Pylint right from the start. To force yourself and others, configure your project in such a way that warnings are reported as fatal errors
Don’t just add them to your project. Follow them. Religiously. Don’t accept any code to be merged or added to your project that violates a warning or error. Simply don’t. Don’t make exceptions. Don’t turn them off temporarily. The time invested in fixing that particular warning is minimal compared to the potential mess and stability you’re giving up.
Discipline yourself and your team mates. If you are in a position of control, don’t accept any less from your team mates. Code that violates a warning should be rejected without mercy.
Get this right from the start and you’re golden.
Continuous integration / Continuous building
I won’t go into all the advantages of having continuous integration. Set up a central service that automatically compiles/builds your project, runs static code analysis and runs all the tests for whatever code is pushed to the SCM server.
It doesn’t have to be fancy at first. It could be just a script that is ran locally on your development system. The important thing is that it runs continuously for every single change.
This is important because if you just set up everything described in the previous paragraph, and you never run it, then people will violate rules, or will commit/merge code that is full of warnings. By having a system in place that continuously runs all these processes you prevent that anyone breaks the build or violates good practices without anyone noticing.
In an optimal situation: establish a rule in your team that no code that breaks this process gets merged.
Automate furiously
When a new project starts, things are simple. It’s small and pretty. Everyone working on the project is up-to-date and has a good overview of things. The project will grow, in terms of code and complexity. You will write more code, more tests, add more dependencies. Maybe you even add more members to your team. All the more reason to automate repetitive tasks furiously.
If a new person is joining the project, you don’t want to have them spend a day setting up the “development environment”. What? A day? Developers are expensive. A day lost can mean losing hundreds of $.
Simplify and automate your development environment up to the point where anyone can get it up and running within an hour. Make your project environment independent. Are you on Linux? Provide a script that installs all dependencies through your favorite package manager. Are you developing in .NET, take advantage of NuGet to install your dependencies.
The people working on Chromium (the browser) took this to extremes (in a good way). They have a special packages that contains everything you need:
Add scripts that run all unit tests in a single go. Add scripts that perform a build and package a release automatically. Add scripts that run static code analyzers. And the list goes on. Found something that requires two or more steps and you did it multiple times? Write a script that does it in one. This will also greatly simplify setting up a build server and/or CI machine. You simply instruct the CI to clone the repository and run the “build” script and then run the “test” script.
Repetitive tasks should be automated to save time and thus money.
Every second that someone is waiting for something to happen, is a second that he/she could have been delivering value. For the sake of business and the developer’s state of mind, keep things quick and avoid unnecessary repetition.
The fallacy
A common response to my words related to this would be: “This can never work on long-term. It will slip.”
Counter response: “Why would it slip? Isn’t it you who let it slip?”
This all comes down to discipline. As soon as you violated your own rules once, your are likely to do it again. Once you disable some thing “temporarily” because it is in your way, you’ve gone down a path that is hard to recover from. The trick is to never do that. You will not enable that thing later. There’s no such thing as later.
Don’t trust yourself and specifically don’t trust future self.