According to a report by Startup Genome, a staggering 90% of startups fail. This sobering statistic shows the immense challenges of building a successful venture. But why is this happening?
For over 5 years, I’ve worked in a startup where I was the first developer. Acquaintances who’ve worked at multiple companies often ask if it gets boring staying in one place for so long, but being part of a growing startup feels like working at several companies all at once.
Here are a few interesting highlights from my time there:
- The startup’s office has relocated more than 10 times.
- We were part of an AWS accelerator alongside a dozen other fascinating startups.
- We explored countless technologies, frameworks, and techniques along the way.
We went from discussing a feature with the СТО and immediately releasing it into production to a 50-page PRD with a gradient release of a new feature in a few months.
However, apart from multitasking, dealing with new tasks, and meeting deadlines, there are some other vital points that a budding startup should consider.
1. Don’t Overengineer
It’s important for a startup to remember to keep its code simple and easy to understand. Let me illustrate this with an example.
We were working on a metric collection feature and decided to make it as general as possible. However, we never used it that way. Instead, we complicated the code, making it harder to maintain in the future.
What does this mean in practice? You should write code that solves the current problem. It should be simple and easy to understand. But, if you think of adding another case, reconsider it. There’s a high chance that you won’t even need it.
Here are the steps you should follow:
- Implement the current solution.
- Identify the limitations.
- Return to the code only if changes are necessary.
A principle with a catchy name perfectly captures this idea: YAGNI – You Aren’t Gonna Need It. Don’t add extra functionality unless it’s truly necessary.
2. Write (Really) Clean Code and Don’t Skip Checks
We had functions that were thousands of lines long! Can you imagine the reaction of developers who saw this for the first time?
It’s a common belief that programmers spend most of their time writing code. However, a scientific study conducted by the Institute of Electrical and Electronics Engineers in 2017 shows that developers spend the majority of their time — 58 % — on understanding code! This is a compelling reason to write clear and readable code. Understanding complex code costs not just one programmer’s salary but also time spent interrogating the person who wrote it or even organizing meetings, which multiplies the costs by the number of participants.
Now, we’re on the same page about the importance of readable code, but “good” code is still an abstract concept.
A great approach is to implement ready-made frameworks into your CI/CD pipeline for code checks and, most importantly, don’t skip them.
For Python, these can include packages like:
-
pylint: Checks the code style to ensure it complies with PEP 8 and PEP 257 standards and even adds to them. For example, functions longer than 1000 lines definitely wouldn’t pass (with a max-function-length rule set to 50).
-
black: Formats the code automatically according to PEP 8, ensuring consistency.
-
mypy: Checks the presence and consistency of data types in the code.
This list is by no means exhaustive, but for starters, it’s more than enough. The key is to make sure you consistently use them. We’ve seen many initiatives where lots of time was spent, only for the tools to be abandoned and forgotten eventually.
Let’s also touch on some well-known principles that align with this approach:
DRY – Don’t Repeat Yourself.
How often have you edited code only to realize it didn’t change anything? You probably edited the wrong code and missed the relevant part elsewhere. Tools like pylint help catch these repetitive chunks of code and ensure they are fixed.
KISS – Keep It Simple, Stupid.
Make your code and architecture as simple as possible. Limiting the number of variables and the length of functions makes things easier to read and understand. Keeping things simple reduces confusion and enhances maintainability.
By sticking to these principles and using automated checks, you’ll save time, improve the clarity of your code, and create an environment where developers can more easily collaborate.
3. Plan for Scalability
Let’s consider an example.
If we use AWS as our infrastructure, sooner or later, we’ll run into limits on the number of API requests. Some quotas can be increased right away, but others have strict limits. This is something we need to take into account when designing systems.
One common exercise in technical interviews is to think about how a system would behave with a million users.
This is a very reasonable question. After all, you’re building a startup that’s going to grow.
Common Bottlenecks:
You can even run a simple mental experiment to identify where the bottlenecks might occur and how we’ll respond when scaling. No one wants to be firefighting when every customer matters.
Load testing is a good tool for addressing this. If we’re building an API, we can simulate heavy traffic using locust.
We increase the load on our endpoints and observe which part of the system fails first. Then, we consider redesigning or replacing that component and continue increasing the load.
Another example: if you already have a certain amount of data in your database.
Just like with load testing, try multiplying the data. How does the insertion process behave? How do the endpoints that read this data perform? If we use an SQL database, might adding indexes or partitioning improve performance?
4. Don’t Rely Only on Yourself
This approach offers a clear double benefit: not only does it help you clarify your thoughts, but it also provides valuable insights from a professional perspective.
Articulating your architecture out loud, even to a non-technical audience, can bring unexpected clarity to your thinking. As David Thomas and Andrew Hunt highlight in The Pragmatic Programmer, the “Rubber Duck Debugging” technique can be a surprisingly effective way to untangle complex problems. By explaining your ideas in simple terms — whether to a rubber duck or an actual listener—you’re forced to think critically about the problem, identify potential gaps, and consider alternative solutions.
However, when you take this a step further and consult with an experienced professional, you can multiply the benefits. A consultant doesn’t just listen—they bring expertise, a fresh perspective, and often the ability to spot weaknesses or opportunities that may not be apparent from within your team. Their experience across different projects and industries allows them to suggest best practices, warn you about common pitfalls, and offer innovative solutions that you might not have considered.
By consulting with an expert before planning new products or scaling existing ones, you’re investing in the long-term success of your startup. They can help you identify risks and areas for improvement early on, saving you from costly mistakes down the line. Moreover, a consultant’s feedback can often align your team around shared goals, providing clarity and direction as you move forward.
Everyone knows that you need to write well-documented code, create tests, and build systems with scalability in mind. Countless books, articles, and videos have been dedicated to these principles. However, at the very beginning of a startup, there’s often no time or resources to follow all the ideal recommendations. That’s why I’ve focused on practical, high-ROI advice—minimizing time and financial costs while maximizing impact.