Why do software projects seem to slow down the closer you get to completion? We think there are a number of reasons - perceptual, intrinsic, and organizational. What are they, and how can you counter them? All of these factors are expressed in terms of “cost” (whether that be money, complexity, or difficulty) and how that changes over time (or age or size of the project).
When you start building a new software project, each additional week delivers many new features. As time goes on, the number of features that already exist has grown, so as additional features are delivered, compared to the expansive set of existing functionality the number of completed features shrink in relative terms to the product’s size as a whole.
Counter: there’s not much to do about this, just be aware of how this may affect your perception of progress.
Cost of inventory
All software has an inescapable maintenance cost. Security patches, critical defect fixes, changes in surrounding infrastructure – these factors of legacy code are unavoidable, and start accruing from day 1. As your project grows, so does the cost of this maintenance, and time spent doing maintenance is time taken away from building new features. It’s tempting to defer it for as long as possible, but this increases the risk of vulnerabilities being exploited, and the maintenance cannot be put off any longer just becomes harder and harder to complete.
Counter: Again, not much can be done about the external changes that your organization has to adapt to, but there are more and less effective ways to manage the response. We advise a proactive stance for security issues. Internally, any simplifications you can make to your product, either in by reducing implementation complexity or by culling unused or low-value features, can reduce the surface area of “inventory” liable to require maintenance. There’s a balance between moving fast and breaking things, and adding so much rigor that a project doesn’t add features fast enough. Experience not only helps here, it can guide you in ways that you can increase the quality without slowing things down.
This is similar to the “Cost of Inventory”, but where that is intrinsic and unavoidable, technical debt is a choice, and can be mitigated over time. As the size and complexity of the project grows, early architectural decisions and abstractions can be tested, and can even fail. Shortcuts taken to “get the job done today” catch up eventually.
Counter: First, realize that accumulating technical debt is a choice, and at times it can be the correct choice. However, avoid carelessness, or cultural issues, that encourage more shortcuts than are strictly necessary. Assuming that some technical debt is inevitable, you should either continuously allocate a small portion of time dealing with tech debt, or allocate time periodically to fix the biggest problems.
At the start of the project, the entire engineering team will often completely understand what is known about the problem domain and the technical solution. As the project grows in size and complexity, the amount of unstated “tribal” knowledge grows. As team members leave and are replaced, the new engineers have more and more to learn before they can become effective. Above a certain project size, new members (or indeed any team member) will never completely understand the problem and technical domains, and will have to rely on others e.g. for cross-cutting changes. Additionally, as project visionaries leave, the vision may leave with them; the remaining team are left to figure out “the map” on their own.
Try to keep your team around for longer; high churn is probably an indicator of other problems too.
Keep project documentation artifacts up-to-date, and relevant. Focus on the reasons for decisions, and on interface boundaries between major components.
Make sure that the onboarding process for new engineers is as automated and well-documented as possible, and use each new hire as an opportunity to improve the onboarding process itself. Their fresh eyes won’t last long.
Larger projects need more engineers. More engineers need more management, process, and managers. More engineers have a higher communication overhead. As your project grows, you may fall victim to your own success.
Counter: small teams working on small projects get more done. If you can keep your projects small and focused, you can likely build a number of them independently and aggregate them together faster than building one single monolith.
On larger projects, keep teams small, focused, and autonomous. Compose your organizational units to align with the way you compose your architecture, and keep inter-team dependencies in control to minimize the network effect.
As your project progresses, you may run into one or more of these factors that contribute to a gradual slowing of that progress. To mitigate this, you may want to consider:
- Build critical maintenance into your plans from the start to avoid the unnecessary cost of unplanned upgrades.
- Upgrade frequently to build the skills and tools to make those upgrades painless.
- Develop a strategy for systematically dealing with technical debt.
- Pay attention to your team’s health, and minimize employee churn.
- Plan for teams to change composition, and build systems to minimize the cost of that change.
- Minimize inter-team dependencies and maximize intra-team autonomy. Specifying and managing interfaces will reduce waste.
Ian Truslove is a co-founder of Cambium Consulting. He specializes in building large-scale resilient data processing systems using tools like Clojure and Elasticsearch. When not hunched over an Emacs terminal, you might find him on a bike in the wilds of Colorado.