Getting started on a new software project, or company is complex. Choices you make early on can be felt through the lifetime of the project for many years, so it’s important to get things right. At the same time, it’s important to get moving and solve a useful problem - to get product/market fit for a company. Often, teams and engineers struggle with the balance between these two goals.
One common pattern is to try to optimize early. In the early stages of any project, you’re not likely to understand what’s really important about it yet. But there are lots of apparently interesting problems that you can work on, so it’s tempting to pick one and try to optimize it. It’s much easier, though, to tune something that has found a fit (or for an internal project, is working), than it is to make something that is highly optimized fit (or work). So in general, it’s ‘fit first, then optimize’.
However, there’s a downside to that that leads to a very common pattern: the hairball. One of the most critical parts of a company or project is understanding when fit is “good enough” and beginning the process of cleanup and tuning. The reality is that this is never perfect, so all successful projects have some degree of cleanup ongoing, but the bad case is that this gets neglected for too long and the project (or company) has to fully “pause” to do a cleanup.
This is more common than you think - many big tech companies have an event like this in their history. There’s never really a good time to shift back to architecture or optimization - once the project is rolling, it’s rolling. There’s no hard and fast rule for when to start the process of cleanup, but as soon as you begin getting traction, it’s worth starting to observe the rough spots and forming hypotheses. Ramping up gradually here is also helpful - don’t “rearchitect” the whole thing at once, that’s no better than letting it fester. Rather - as use cases and patterns emerge, try small changes and cleanups to streamline them, and see if they’re repeatable. The mantra “One, two, platform” is useful here - never build a framework or platform until you’ve seen at least a few instances of what it’s meant to automate.
A related pattern is “we don’t have fit so let’s design something that can cover all cases in our area of interest while we wait for inspiration”. This leads to the opposite kind of failure, a system that is so broad that it’s not really useful for anything. It’s good to not be optimizing early, but under-optimizing is bad too. The goal is to find a tight fit between the code you build and the problem you are solving - the ideal (never achieved) is that you never write a line of code that you didn’t need. Building a big general framework before you understand your problem or customer well is just asking for a different kind of waste.
So…find fit first! Always the best thing to do, and then optimize slowly…but do optimize.
Love this post, Sam.
> Building a big general framework before you understand your problem or customer well is just asking for a different kind of waste.
There is so much art than science to this and where the wisdom of senior engineers, who have seen similar evolution, is invaluable. A failure scenario I often see is that once the team does decide that a framework/platform is necessary, they go for "boil-the-ocean" strategy and won't leave enough room for flexibility which really comes back to bite the team.