Imagine walking into this: “About 4 million lines of PHP code, written by underpaid, sometimes not well meaning, freelancers and students over the span of 8 years. The CEO wrote a large part, but stopped learning new techniques around 2004.”
That’s how bad tech debt can get when a startup is run without considering that all of those messy shortcuts will eventually have to get cleaned up.
If you’re in a technical role at a startup, though, you know that avoiding tech debt isn’t as easy as it sounds. Startups often have a lot of development milestones to hit in very little time. Getting a startup to MVP with limited personnel and budget sometimes requires cutting a few corners, knowing you’ll have to do some refactoring in the future.
But moderation is important. When you’re taking on tech debt, you’re building a mountain that you’ll have to climb before your company can scale. All the engineering time you’re saving now will have to be paid back – often with interest. Many developers that have worked on projects with significant tech debt have stories like this:
I’ve worked in a couple of large and messy codebases where we undertook a big refactor, it took longer than expected and then the resources were pulled to other things before it was completed. Net result: an even larger, messier and harder to understand codebase!
In other words, when tech debt gets too big, fixing it becomes very tricky even when you have a lot of resources to throw at the problem. For startups, the best approach is to avoid taking on any unnecessary tech debt, so let’s take a look at six things startups can do to minimize or eliminate the tech debt you’ll have to pay back before you can scale.
This advice may sound obvious, but following it can be challenging. It’s not always obvious what debt you need. Building a startup almost always requires making some compromises in the early days, but as tools and technologies change and evolve, it can be difficult to figure out which compromises you actually need to make.
Consider, for example, the transactional database. Just a few years ago, startups had to make a difficult choice, and one that unavoidably involved compromise: do you opt for a NoSQL database that’ll scale quickly and easily, and deal with the potential consistency problems and potentially higher costs now? Or, do you go for something like PostgreSQL that’ll be reliable, easy, and free for your developers to work with now, but probably challenging to scale in the future?
Neither of these options is ideal; the NoSQL approach can come with unpleasant down-the-road problems such as dirty reads, phantom reads, write skew, etc.. But the Postgres approach is also taking on a kind of debt, as it’ll have to be manually scaled later. As education startup Kami learned during the pandemic, scaling Postgres can be painful:
“We knew how complicated it would have been to stay with Postgres and set up sharding. There would have been a constant drag of managing multiple shards of data. No one on our team wanted to go through that manual labor. And even if we did set up sharding, we weren’t going to be able to grow our business 10x.”
Today, though, it’s possible to avoid taking on a lot of tech debt simply by opting for the right stack – although what constitutes “right” will depend quite a bit on what you’re building and what you’re already familiar with. In almost any stack, though, no-compromise options such as CockroachDB serverless allow startups to get the best of both worlds: CockroachDB serverless is free, it’s easy for any developer who knows SQL to use, and it’s ACID compliant, but it’s also distributed and cloud-native and built to scale with the same hands-off ease as popular NoSQL database options.
The transactional database is just one part of your stack, of course, but similar solutions now exist for almost every layer. The business logic of your application, for example, can be moved to a cloud-based serverless service such as AWS Lambda, Google Cloud Functions, or Azure Serverless Functions, allowing for nearly infinite scalability without requiring much of an up-front investment in terms of time or money.
It is critical, then, for startups to be aware of their options. Compromises that companies like Kami were forced to make may now be completely avoidable thanks to new tools. In particular, the proliferation of serverless offerings across the stack have made it possible for startups to build for scale before they have to pay for scale. That, in turn, allows them to avoid taking on the tech debt of building with hard-to-scale technologies just because they’re the cheapest and fastest way to get to an MVP.
Teams can now build with serverless options are just as free and just as easy to get to MVP while also offering cloud-native automated scale to keep costs minimized and application performance consistent whether the application is dealing with 360 concurrent users or 360,000 concurrent users.
Avoiding tech debt in the early stages of a startup is thus sometimes a function of keeping your ear to the ground and being aware of all of your options. Less than a year ago, there was no option for a free, serverless distributed SQL database. Now there is. Being aware of that option (and others like it) can help you avoid taking on tech debt and making compromises that you no longer need to make.
In the early stages of a startup, it’s pretty common for developers to have to “wear a bunch of hats.” For example, developers at startups often pull double-duty assisting with ops until the company is big enough to afford dedicated IT ops or DevOps personnel.
But startup development timelines tend to be short, with lots of features on the roadmap and limited runway necessitating quick turnarounds. Any time developers are spending on ops work is time they are not spending building your application, and the less time they have to build, the more corners they’ll need to cut to hit their deadlines. And every cut corner is a little piece of tech debt.
For that reason, opting for managed services can sometimes be the most economical choice, even when that means absorbing a higher up-front cost. You may not always save money with managed – that’s a calculation that you’ll have to do for yourself – but it’s important to factor in the costs that will be associated with having your developers learn about and manage operations.
That cost goes beyond the development time your devs will miss while they’re doing ops work. Opting for a managed service puts ops into the hands of experts, and it usually comes with priority technical support to facilitate integration, which can mean a smoother integration process, better application performance, and faster resolution of ops issues when they do arise.
It’s possible to take out tech debt without even realizing it when you make design choices or opt into services that will be difficult to change away from later.
While there are many examples of this, one of the most common is locking yourself into a single cloud’s ecosystem. There are often compelling reasons to do this in the beginning, as you can take advantage of the efficiencies on offer when you’re connecting AWS Lambda functions (for example) to other Amazon services like Aurora or ElastiCache or Redshift.
But in the long term, what if GCP or Azure becomes the more affordable option? Or, what if you realize that offering the most reliable service to your users is going to necessitate going multi-cloud? Suddenly, a huge debt has come due, and your team will have to figure out how to – just to pick one example – migrate its Amazon-only database to something that can support AWS and GCP without causing a whole host of other problems.
That’s why whenever it’s possible, it pays to make design and tool choices that will allow you to remain flexible. Sometimes, this requires trade-offs, but at other times, it’s possible to get the same kind of performance with increased flexibility simply by choosing a different tool. For example, CockroachDB is easy to connect to AWS Lambda functions, and it supports GCP and Azure in addition to AWS. So even if your functions are still built and deployed on AWS, having a cloud-agnostic database would allow you the flexibility to switch to another cloud or go multi-cloud in the future without having to change out your database.
As engineers, we’re always tempted to try to reinvent the wheel. That constant drive for improvement is the force behind a lot of innovation, but for a startup to succeed, it has to be tempered with practicality. While a bespoke solution might be perfect, there’s often a plug-and-play solution that’ll give you 99% of the functionality you want without costing you any development time.
For example, Starburst is a data analytics engine that gives customers a single (and fast) point of access for all of their data. To ensure good performance for its customers, Starburst needed a multi-region relational database. The company certainly could have tried to build a bespoke solution. But as Starburst VP of Engineering Ken Pickering put it: “Why would I have my engineering team try to solve the multi-region problem when there’s an engineering team out there that’s already built a solid solution [in CockroachDB]?”
“We needed to make defensive, smart technology solution choices,” Pickering said, “because we are on the hook for our customers’s data.” The same is true for most startups, even if you’re not yet on the hook for many customers – you need to make smart choices that defend your developers’ time.
To grow fast, startups need to focus on solving the core problems their team was assembled to solve. If your developers get sidetracked into building bespoke solutions for problems somebody else has already solved, they’re going to be so pressed for time that they’ll have to cut corners developing your product’s core features. That’s tech debt that you’ll ultimately have to repay.
While we’ve focused on bigger-picture strategic ways to avoid tech debt using (for example) the solutions you choose for your tech stack, a lot of tech can be traced back to code that’s either sloppy (often because it was rushed) or sloppily documented (often because it was rushed).
If your goal is to avoid tech debt, it’s important to ensure that you’re implementing and following coding best practices from day one – or as close to day one as you can possibly get. We won’t say too much about these, as you’re probably well aware of their importance already, but this should include repeatable, formalized systems to ensure that all code is:
While all developers are aware of this, these are all corners that are easy to cut when your development team is just a few people (or even just a single person). When a deadline is fast-approaching, it’s easy to decide that you don’t need to comment and/or document your code, but every time you skip something like that, you’re taking on a little tech debt that the company will have to pay down someday.
Of course, whether or not developers have the time to follow all of these best practices isn’t always in their hands. Often, development timelines are passed down from the top, so we’ll end with one final piece of advice that even CEOs need to embrace:
The real key to avoiding tech debt is to get good at balancing your needs in the present with your goals for the future. Actually doing this is challenging, and requires incorporating all of the things we’ve already discussed in this post, and bringing that knowledge to broader discussions about the company goals and timelines.
For example, if your CEO is setting development deadlines that will be challenging to hit without taking on some form of tech debt, technical staff need to be able to identify and communicate those trade-offs so that they can be factored into budgeting and planning. A CEO who wants to ship two major features this quarter may be very willing to live with shipping just one if the alternative would mean spending all of next quarter dealing with customer support headaches and refactoring rushed, buggy code.
Trying to optimize for the best outcomes while balancing limited time, skillsets, and budgets is a real challenge. But then, nobody said building a startup was easy! It’s unlikely that you’ll be able to totally avoid taking on tech debt, particularly in the rushed early days of startup life. But if you can make good choices in your stack and build good internal guardrails to avoid cutting coding corners or solving problems you don’t have to solve, you can at least be confident that you’re headed down the right path.
(And if you’re looking for a database that’ll help you avoid tech debt by being easy to implement and automatically scaling as you scale, check out CockroachDB serverless – you can start instantly, for free).
We’ve been building for this for three years, said Naveen Molloy, COO of Bitski, a startup company based in San …