DEV Community

Jake Marsh
Jake Marsh

Posted on

Optimizing for Iteration: Choosing Your Early Startup's Tech Stack

Product development is hypothesis testing. This is especially true in the early stages of a company when you need to confirm or reject your hypothesis as quickly as possible.
This process is then repeated until you (hopefully) reach product-market fit. To get there, your team needs to be able to work and build at a pace that allows for this constant and rapid iteration.

Your companyโ€™s tech stack will immediately be a fundamental part of how your engineering team works and operates. This is even more important in a small company where the engineering team represents
an outsize portion of the team as a whole.

Many people will recommend to early founders that they use the technologies they know and write in whatever language they're familiar with. Although this is good advice, like all other decisions it should be weighed carefully.
While ideally you're fully familiar with the stack you choose in order to move as fast as you can, you should also be putting some thought into how these choices will affect you and your engineering team moving forward. For example, it may not always be best to pick the brand new (and unproven) technology on the block when you need to scale quickly and painlessly.

What to Optimize For

The overall goal can be summarized as this: minimize time spent on non-feature engineering work.

For every hour you or another engineer spend configuring, debugging, or learning a new system,
that's an hour not spent iterating on the product to maximize learnings and value delivered to your users.

Here are the things to aim for when choosing your startup's early tech stack.

๐Ÿคฏ Easy to understand

On a small engineering team, everyone is going to be working everywhere in your codebase. Additionally, if things are going well,
you'll be adding new engineers to the team fairly regularly. For these reasons, your codebase and systems should remain easy to understand and get started with. No one wants to spend multiple days banging their ahead against a spaghetti codebase or tangled dev environment.

There's a few things that can help with maintaining the grokkability:

Well-known languages or frameworks

Technologies often come and go quickly in software development. However, there are those libraries that are nearly ubiqitous. These are the languages and frameworks
that the majority of software engineers would be at least vaguely familiar with. Examples would be Rails or React, both incredibly popular libraries used and supported by a large number of people and companies. This also means they have large and healthy ecosystems surrounding them.

Typed languages

Large refactors of core business logic are common when you're an early-stage company iterating often on your product direction. These are no longer nerve wracking when you always know what data youโ€™re working with and when.

Self (or well) documented code

While the concept of true "self-documenting code" is fairly controversial,
it can still be valuable when not taken to an extreme. Write and architect your code so that its purpose is clear and its execution path is easy to follow.
Leave clarifying comments when blocks of code become overly complex out of necessity. As mentioned above, typed languages also help a lot here.

๐Ÿ™Œ Our Recommendations
  • Rails โ€” The simplicity of Rails' MVC model, coupled with the easy to read and write language Ruby, means it's easy for most engineers to become comfortable working in Rails and shipping production features. Rails also continues to power many well-known, large-scale applications such as Shopify and GitHub.
    • Caveat: Although Ruby is not typed by default, there is a new static type checker called Sorbet.
  • TypeScript โ€” Defining your types and using TypeScript strictly results in code thatโ€™s easy to parse and safer to modify.

๐Ÿ”Œ Plug and play

You should favor technologies that are easy to get up and running. On a small team without a dedicated devops team,
engineers should not be dedicating time to setup complex and/or custom infrastructure. There are two things you should
be on the lookout for:

A large community/ecosystem

If it even needs to be said, open-source is your friend. High numbers of users (downloads) and contributors are a good signal
of a healthy ecosystem for an open-source library, meaning bugs should be resolved fairly quickly and new features will likely be shipped. A few quick (and imprecise) measures we use to quickly get a read:

  • The number of stars a repo has on GitHub
  • The date of the last significant update
  • The number of outstanding (and/or stale) issues and pull requests.

"Setup-free"

This one is fairly self-explanatory. I provide the quotations because I've yet to find a library that is truly and literally setup-free for anything beyond the most basic use case.
However, it is still good to aim for libraries that will not require a large amount of someone's time to configure.

None of this means, however, that you should always avoid the route that requires a little more work initially if it pays off in the long run. We'll talk more about that below in "Automate everything".

๐Ÿ™Œ Our Recommendations
  • Rails โ€” One of our picks again. The long history and large community around Rails means you can find a gem for practically anything your application could need to support. Many of them are even included in Rails itself.
  • Next.js โ€” Next brings a Rails-like simplicity to handling an isomorphic React application. Data fetching and page definitions are unified, while you get to continue using any React patterns or components you prefer.

๐Ÿ”„ Aim for reusability

Every time you re-use a block of code, you're saving some fraction of the time originally spent writing and testing it. It also reduces the surface
area for bugs and for engineers to learn about in your codebase. Here are some ways to aim for reusability across your application's codebase.

Share languages or frameworks

Sharing a single language or framework across multiple parts of your system can increase the options for code sharing. JavaScript is often the
top choice in this sense, as it can be used for everything from your database ORM to your native mobile client.

Build modularly

When multiple parts of your system are using the same core language or framework, it becomes increasingly advantageous to build modularly.
While this always helps with separation of concerns and unit testing, it can now open up new ways to share and re-use code across your application.

For example, maintaining and publishing all of your app state across multiple clients becomes much more manageable when you can use
the exact same Redux code across web, iOS, and Android.

There are limits to reusability, however, as complexity often grows exponentially in the same direction. It is important to keep that in mind
as you still aim to achieve the first goal we discussed, "easy to understand".

๐Ÿ™Œ Our Recommendations
  • React โ€” React has become an incredibly popular framework choice to go alongside a universal JavaScript application. Many libraries exist to support code re-use across frameworks and platforms.
  • React Native โ€” RN is one of those platforms that encourages code re-use with other React applications. Write your native mobile applications (iOS, Android) using your same modules in familiar architectures.
  • Gatsby โ€” Another React-related library, Gatsby allows for easy creation and publishing of static websites using the same React files and patterns. Great for maintaining a scalable marketing site.
  • Lerna โ€” Lerna helps to more easily maintain a monorepo of inter-dependent JS packages. Easily bump while keeping versions in sync, as well as publish to NPM.

๐Ÿ›  Automate everything

As we discussed above, complex systems and configurations should be avoided whenever possible. However, there's one exception: automation.

Likely the place best suited for automation is your CI/CD process.
This should include everything from linting, to testing, to production deployment. Ideally it's happening quite often, but it can also include any
number of steps each of varying complexity.

Automating any task is going to require a varying amount of upfront effort. However, the ongoing time saved is a multiple of
the frequency and duration of these tasks. Additionally, the ongoing bugs and confusion that would arise from doing every process manually
are immeasurable.

๐Ÿ™Œ Our Recommendations
  • Terraform โ€” Terraform helps us to fully manage our cloud infrastructure in source-controlled and peer-reviewed code. This allows for consistency and self-documentation, while giving us much more confidence in our deployed systems.
  • Docker โ€” Docker allows us to pre-define our various service environments (we have around 12) as reproducible and deployable containers. Again this allows for consistency, self-documentation, and confidence in our systems.

โ—๏ธ Are you a software engineer?

At Monolist, we're following these tips to rapidly ship new features that help software engineers be their most productive. If you want to try it for free, just click here.

Top comments (5)

Collapse
 
cubiclebuddha profile image
Cubicle Buddha

Iโ€™m so glad to see you recommending Next and TypeScript. Why not TypeScript on the backend too?

Collapse
 
jakemmarsh profile image
Jake Marsh

Honestly, mainly just due to our existing familiarity with Rails and its huge ecosystem. We knew we'd be carrying out a wide array of tasks on the server, and there's a gem for everything.

I know this is largely true for the Node/Typescript ecosystem as well, but I've personally found Rails to be more plug-and-play.

Collapse
 
cubiclebuddha profile image
Cubicle Buddha

Theyโ€™re probably about equal when it comes to availability of plug nโ€™ play libraries. Note: a lot of teams find that theyโ€™re more productive when they can use the same language throughout the whole stack.

Collapse
 
bootcode profile image
Robin Palotai

Great article!

Re everyone working on everything in a small team. I was a tech lead of a small team of five, and my observation is that natural code ownership happens organically even at this size. Typically a single part has up to two major contributors.

I can confirm that using the same language, along with a common style and best practices guide, helps peers access the code when needed.

Collapse
 
steelwolf180 profile image
Max Ong Zong Bao

Hmmm...I find it interesting that the bulk of your recommendations are Javascript based.

For me, I'm more of the camp of depending on the founding team for their tech side.

If the bulk of them has experience .Net, PHP or Python I would just go with those and slowly migrate my stack to cater for scale.

As long as my main goal is to get my products or services out there, the type of stack is not that important.

Sure I do have biases for Python-based stacks but my core is whatever works to deliver the products or services.