Optimizing for Iteration Part 1: Deciding Your Stack
As we’ve discussed in a previous blog post, 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.
This is the start of a three-part series of our suggested engineering decisions and methodologies to maximize your team’s ability to iterate. From the tech stack, to how to organize your code, to daily best practices, we will go into detail on how we operate here at Monolist in order to quickly ship features our users love.
Part 1: Deciding Your Stack
Your company’s tech stack will be a fundamental part of how your engineering team works and operates. Your engineers should feel comfortable working in any part of the stack, especially when your team is small. This is why it’s important to optimize for the right things when choosing your technologies. Let’s dive into what we use here at Monolist and why.
Server: Ruby on Rails
Ruby on Rails is an incredibly popular web application framework first released in 2005. It uses, well, Ruby, as its programming language. Ruby is a fairly easy to read and write dynamic language. Coupled with Rails, web applications can be built and iterated upon easily. However, you do not have the safety and self-documentation of statically typed code.
Due to Rails’ age and popularity, it also has an incredibly strong community and ecosystem. Rails itself has optional modules for many of the common things your app will need to implement like an ORM, WebSockets or automated emails. We use it for all three. There is also a large number of other open source gems available and compatible with Rails.
Although the dynamic nature of Ruby can lead to some obvious errors, when paired with Rails they allow for quick and easy development of features across a large number of domains.
Client: Typescript, React
Next is a React framework from Zeit. It takes care of many things from server-side rendering to code splitting. It makes it extremely easy to build performant, isomorphic applications. Additionally, there’s a large community and plugin ecosystem that provides support and options as your app grows in complexity.
The earliest iterations of Monolist were built on a home-grown isomorphic framework. It was riddled with bugs and hard to debug. It made the developer experience quite painful, and we found ourselves wasting too much time fiddling with it and not enough time focusing on the product. We decided to go with Next for the reasons above, and even ended up getting some love from the Zeit CEO when he stumbled upon our Next-powered app.
Gatsby is a React framework for generating static sites. Although Next also has this capability (and Gatsby can do many of the same things as Next), we still prefer to use them both for different purposes. Since they both use React and support TypeScript, we’re still able to share any common modules between them.
Gatsby uses GraphQL to pull data from a variety of sources when generating your static files, such as an API or even local Markdown files. This makes it fairly versatile in what it can be used for. Our marketing site is powered by Gatsby, and we also use various community-supported plugins to manage our blog posts and job listings via simple Markdown files. This even allows non-engineers to update them fairly easily.
Mobile: React Native
React Native is a very popular library also written and maintained by Facebook. It works with largely the same paradigms and modules as the browser-based React, making it an extremely strong candidate when considering the ability to write code once for all platforms. At Monolist, we share our internal business logic between web, iOS, and Android, allowing us to easily make any necessary changes across all three. We’ll talk more about that in Part 2.
As your company grows it may indeed not remain the best option. But when your team is small, it allows you to work within familiar concepts while reusing large portions of your code. This enables any engineer on your team to iterate quickly on any platform.
What to Optimize For
We’ve optimized for three things as we’ve decided upon the various parts of our tech stack:
- Easy to understand: look for languages or technologies that help ensure your codebase remains workable for both new and existing engineers. This could take the form of typed languages, well-known languages or frameworks, code sharing (DRY), or self-documenting code. Any time spent trying to debug, understand, or explain cryptic code is time wasted.
- Plug and play: favor technologies that are easy to get up and running. This can mean a large ecosystem with libraries or plugins for your later needs, or just tools that require little to no initial setup. The goal is to minimize the time your team spends on non-feature engineering work.
- Abstract away the hard parts: at the early stages of your company, you don’t have the time to spend focusing on smaller details like networking, SEO, or optimizing milliseconds off of your TTFB. Although they are “black boxes”, technologies that abstract these things away are your friend for now.
In Part 2 of this series we’ll cover some of the topics that we’ve touched upon here, including how to manage and organize your code for pain-free refactors, as well as maximizing shared code between your repos.
Want to give Monolist a try?
Just request access. Once you gain access, you'll be able to invite your friends and coworkers to skip the waitlist.