How to save money in software projects?

Building software is expensive. Software developers and other necessary professionals have high salaries. Coding is time consuming. We all know that.

So how we can save money? By hiring cheaper developers? By hiring developers who code faster? By working longer hours? Well, not really, those solutions, if even rational, are only about moving costs from one place to another.

Biggest savings we can get by strictly controlling the scope of the project.

There is too much waste in the industry.  Many teams build features that nobody uses. Developers are solving abstract problems that do not really exist from business domain perspective. Business is asking for things that never convert to added value. Software should be an investment but often is only a cost.

How to prevent that? It’s hard, but here is my advice:

  • Think at least 3 times if the idea converts to value before starting building. Ask for feedback. Gather data. Starting a project based solely on intuition is a risky business. Without solid data, it’s like gambling. Money can be lost in this game, so be aware of how much risk you can accept.
  • Ok, so you have a proven arguments that the idea is wort building. Can it be achieved simpler? Maybe there is already a tool that you can use? Affordable SaaS platform? Open-source software? Think 3 times about how to make it efficiently.
  • If the project is not a standard thing that you did already – start small. Do the research. Build proof of concept of the unknown parts. Define solid technical foundation for the project. It will be harder to change it later. Check what works and what doesn’t in your case.
  • Do not involve big development resources before you know what you expect from them. Prepare the project. Know your budget. Know your timeline. Check with technical people if the assumptions are realistic. Make at least a rough estimate. Think about the risks that may impact the estimate. Are the risks acceptable?
  • Verify the results often and early. There should be something usable produced as soon as possible. Start from the core business. Do not build “nice to have” things when core functionality is not ready. Nice to have things may kill the project. Be strict about the scope and constantly challenge it. Is this story or sub-task worth doing? Asking that question is not a signal of laziness, it is a signal of caring about the budget, the timeline and the impact.

Based on my experience, especially from start-up and innovative projects, those are the rules that are often forgotten. We tend to rush, but as a result, instead of being quicker we may be slower because of loosing the correct way. We want to be agile but we forget about planning and create too much chaos instead of agility. We say that we care about quality of the product so we build the whole plastic toolbox instead of just one precise tool at time. We let engineers to own the estimates but we are not communicating the constraints and not controlling the scope.

Those are the major sins that I try to address by the above rules. I hope that you will find it useful also in your project. and .NET Expressions

I was troubleshooting an interesting bug recently thanks to which I’ve learned a bit more about and expressions in .NET.

The situation was that Hangfire dashboard looked correctly, we had all jobs registered as expected. But what was actually executed by the scheduler for each job was same logic, which was supposed to be executed only for the last job. Nasty bug. We were not yet on production with, but still it was quite an unexpected behavior to see.

Reason was that we were wrapping each job in a class called JobRunner. This class was adding some generic functionality to update UI progress bars when jobs are running. Our code looked like that:

JobRunner runner = new JobRunner(myJobClass);
RecurringJob.AddOrUpdate(myJobClass.JobId, () => runner.Execute(), myJobClass.CronExpression);

Crucial thing to understand about Hangfire is that the what we pass to AddOrUpdate method is not a function to execute but an Expression describing the function to be executed. See this thread for difference between Expression<> and Func<>.

runner instance is not kept in memory or serialized. When Hangfire executes the job, it needs to create the instance by calling the constructor of given type. Constructor arguments are resolved from IoC container. In our case constructor argument was of type IJob. This interface was providing properties like JobId or CronExpression. So what was happening when EVERY job was running, was firsts implementation of IJob found in the container injected into a JobRunner. For each job same implementation of IJob was injected. And here we are – all jobs magically are executing same logic…

Now it seems quite obvious but it was necessary to learn couple of rules along the way to understand that behavior. It seems to be a common misunderstanding as there is even a comment about people making that mistake in source code, see Job.cs .

I hope this case study will help someone to avoid similar traps.