How to Eat the World

5 Lessons in Startup Software Engineering


June 29, 2023

@gdbroman


Building excellent software is all about speed. Big technology companies have entire departments dedicated to “Engineering Productivity,” but best-practices at large engineering organizations with product-market fit are often opposite to what's right for early-stage startups. A large company should optimize for reliability because its biggest risk is losing customer trust, whereas a startup should optimize for speed because its biggest risk is running out of runway. Which is why successful startups figure out a way to ship and re-ship – insanely fast. What follows are 5 principles startups should follow to produce software at speed.

1/ Minimizing Admin

Engineering time is expensive and best spent on solving problems – not administration. Whilst a project management tool like Jira is necessary to keep a project organized – especially as head count increases – just like with CI and code reviews, it is important to not use it tyrannically.

For instance, if a piece of code is broken, a ticket should not be required to fix it – or you will disincentivize on-the-fly improvements in the long-term. Create a shipping culture, not a PM culture.

2/ Pacing

Inexperienced engineers will haste 90% of a project and spend 10 times as long completing the remaining 10% – or never finish because of tech debt. Software that “almost” works is useless. Yes, we're building an MVP with a minimal set of features, but those features still need to work!

Finishing the last 10% of a software project requires the hard-earned wisdom of knowing what work needs to be paced, and is thus what distinguishes experienced engineers from inexperienced such.

3/ Enforcing Uniform DX

A uniform developer experience is a forcing function to keep a codebase simple. New hires should be made aware of the codebase conventions and pointed to a concise README that stipulates them.

Part of a uniform DX is having a centralized CI environment which builds, lints, tests, and/or compiles code changes. It is easy to go overboard with CI since runs on every PR and quickly becomes expensive. So it's important to select a minimum effective dose of CI which doesn't negatively impact speed. Tests, for instance, should be used sparingly as they're expensive to run (server costs), and expensive to maintain (developer time). Prioritize strong types and linting instead as they're cheaper and can be set up to continuously run in engineers' IDE. But since every developer might have slightly different local setups, there's still value to centralized linting.

If you can unify the machine, OS, and IDE your engineers use, you should. Unifying is not always possible; for example, if the app is a cross-platform desktop application, in which case it can be an advantage to have different setups for testing purposes. But unifying every engineer's setup will save developer time worth more than whatever the machines cost.

4/ Sensical Code Review Culture

Code reviews are valuable because they:

  1. Ensure codebase quality
  2. Share technical knowledge amongst colleagues
  3. Keep everyone up-to-date of what is going on in the codebase

But code reviews are hard, and they don't always make sense. You want to suggest worthwhile improvements and generally avoid nits. As a rule of thumb, code reviews should be reserved for:

  • A) Big changes
  • B) Changes to another colleague's code
  • C) Changes by new hires (to accelerate their integration)

Furthermore, it sometimes makes sense to merge PRs before a feature is fully complete – either to unblock peers, or to apply migrations and infrastructure changes. And if you're unsure if something needs a review, asking a colleague for input is almost always worthwhile, if anything to form a culture of respect.

5/ Keeping the Team Small and Complementary

Early-stage startups need to do a lot with little, which means hiring T-shaped engineers with product and people skills. This type of engineer go under many different names, but two common ones are product engineer and founding engineer.

But if you are going to hire more than one or two engineers, it is crucial that their skill sets are complementary. For example, if you are more of a backend engineer, hire a frontend engineer (given it's needed). If you know data, hire someone great with design. Whilst you want generalists in the broader sense, the risk with hiring engineers without any technical specialization is that you get a mix of good code and bad code living throughout the stack. It is far better to have one specialized engineer able to address each area of the stack.

Furthermore, defining complementary roles within your company will keep employees from working on the same areas, reduce feelings of rivalry, and promote a culture of respect, since everyone is doing what no one else could.