No more crunch time: the better way to meet your deadlines
Deadlines are hard to navigate.
On the one hand you risk crashing into the rocks of late delivery, and on the other you risk drowning in a whirlpool of broken software and technical debt.
And if you end up working long hours—if your only solution is crunch time—you risk both burnout and technical debt at the same time. Metaphorically, you risk drowning in a whirlpool full of fire-breathing rocks.
Given too much work and not enough time, you need to say “no” to your boss and negotiate a better set of deliverables—but what exactly should you be aiming for?
- You could argue for maintainable, well-tested code, and deadlines be damned. But what if the deadline is important?
- Or you could argue for meeting the deadline no matter what, even if that means shipping untested or buggy code. But doesn’t the code need to work?
This dilemma is a false dichotomy. Quality is situation-specific and feature-specific, and rote answers aren’t enough to decide on an implementation strategy.
The real answer is to prioritize the work that really matters in your particular situation, and jettison the rest until after the deadline. And that means saying “no”: saying no to your boss, saying no to your customer, and saying no to yourself.
No, all the code can’t be perfect (but this part needs to be rock solid).
No, that feature isn’t important for addressing our goals.
No, we can’t just add one small thing. But yes, we will do what really matters.
Let’s consider two examples: the same task, but very different goals and implementations.
Deadline #1: Raise money or lose your job
I once worked at a startup that was starting to run out of money: our existing business was not working. Luckily, the co-founders had come up with a plan. We would pivot to a trendy new area—Docker was just gaining traction—where we could build something no one else could do at the time.
Now, this meant were going to be building a distributed system, a notoriously difficult and complex task. And we had a deadline we had to meet: we were going to do a press campaign for our initial release, and that requires a few weeks of lead time. And of course on a broader level we needed to make enough of an impression that we could get funding before our current money ran out.
How do you build a complex, sophisticated piece of software with a short deadline? You start with your goals.
Start with your goals
Our goal was to demonstrate the key feature that only our software could do. We decided to do that by having users walk through a tutorial that demonstrated our value proposition. If the user was impressed with our tutorial then we’d succeeded; we explicitly told users that the software was not yet usable in the real world.
Based on this operational goal we were able to make a whole slew of simplifications:
- A production system would need to support parallel operation, but our tutorial only needed to be used by a single user. So we implemented the distributed system by having a CLI that SSHed into a machine and ran another CLI.
- A production system would need to handle errors, but our tutorial could run in a controlled environment. We created a Vagrant config that started two virtual machines, and didn’t bother writing error handling for lost connections, inaccessible machines, and so on.
- A production system would need to be upgradeable and maintainable, but our tutorial would only be run once. So we didn’t bother writing unit tests for most of it, and focused instead on manually running through the tutorial.
Now, you might be thinking “but you’re choosing to abandon quality”. But again, none of the things we dropped were relevant to our goal at that time. Spending time on “best practices” that aren’t relevant to your particular situation is a waste of time.
We were very clear in all our documentation and marketing efforts that this was a demonstration only, and that we would be following up later with a production-ready release.
Prioritize features by goals
Even with these simplifications, we ended up dropping features to meet the deadline. How did we decide which futures to drop?
We started by implementing the minimal features needed to demonstrate our core value, and so when we ran out of time we dropped the remaining, less critical features. We stopped coding before the drop date (a week or a few days, I believe), and focused just on testing and polishing our documentation.
Dropping features was quite alright: the idea was good enough, the tutorial was compelling enough, and our VP of Marketing was skilled enough, that we were able to raise a $12 million Series A based off that unscalable, unmaintainable piece of software. And after the initial release and publicity push we had time to implement those later features to keep up our momentum.
Deadline #2: Production-ready software
Once we had VC funding we rebuilt the product with the same set of features, but a very different goal: a production-ready product. That meant we needed a good architecture, an installer, error handling, good test coverage, and so on. It took much longer, and required a much bigger team, but that was OK: we had a different goal, and enough funding to allow for a longer deadline (again, based on publicity needs).
Even so, we made sure to choose and prioritize our work based on our goal: getting users and customers for our product. Our initial prototype had involved a peer-to-peer storage transfer mechanism, and making that production ready would have been a large R&D exercise. So in the short term we focused on cloud storage, a much simpler problem.
And we made sure to drop less important features as deadlines approached. We certainly didn’t do a perfect job, e.g. we dropped one feature that was half-implemented. We would have done better not starting it at all, since it was less important. But, we succeeded: the code was maintainable, people started using it, and we didn’t have to rely on crunch time to deliver.
Beyond universal solutions
There is no universal solution that will work everywhere, no easy answers. All you can do is ask “why”: why are we doing this, and why does this matter?
Once you know your goals, you can try and prioritize—and negotiate for—a solution that achieves your goals, meets the deadline, and doesn’t involve long hours:
- Drop features that don’t further your goals, and start with the most important features, in case you run out of time for the rest.
- Write high-quality code in the places where it matters, and drop “quality” where it doesn’t (what Havoc Pennington calls “professional corner-cutting”, emphasis on professional.)
- And if you still have too much work, it’s time to have a discussion with your boss or customer about what tradeoffs they actually want.