Great products are not born overnight. Sure, most start with a spark of the imagination, but it takes years of learning, tight customer feedback loops, and lots of rapid iteration to make a truly great product. Even though at some level we all know this - many of us still gravitate towards slow and unproductive ways of building products.
I’ve noticed a dangerous trend among some engineers - developing extremely strong opinions about the minutiae, opinions that they’d rather spend all day fighting for and defending instead of just accepting another point of view to let the team move forward.
When starting my latest startup, Builder.io, I knew we would need a very senior team. The product is very technical, trying to turn intuitive visualizations into very optimized code. But I had a major fear - too many opinionated engineers in a room can quickly turn into a flame war inferno.
On top of that, many adopt a mentality that “if I am doing something related to code, I am doing my job”. Aka starting long debates on pull requests, slack storming about best practices, or just digging into the work of others uninvited. But while these conversations do have their place, this is only rarely the best way to build products.
Every business lives and dies on its ability to rapidly innovate. The world moves too quickly not to, and competition is always at your heels. At a startup, you can feel it far more than in a larger company, because there are far less layers cushioning you from the brutal realities of what a business needs to survive. Features need to ship, customers need to be happy, $ needs to come in.
But even then, you’ll have people who want to worry about the proper naming, abstraction, usage, etc of every line of code - far past the point of diminishing returns.
To be fair, writing clean and readable code is important, and a skill that needs to be learned. A junior developer needs to think hard and work hard to make code clean and readable, it’s not something that comes naturally. But I keep noticing as many grow more senior, sometimes we start getting far too in the weeds -- myself included.
On top of that, it can be very hard to remove yourself from this mindset even if you acknowledge you have it and that it can get detrimental. Zen Buddhism has a name for this, Shoshin, meaning “beginners mind”.
In the beginner’s mind there are many possibilities, in the expert’s mind there are few
-- Suzuki, Shunryu (1970), Zen Mind, Beginner’s Mind
The startup world has a popular saying -- “fail faster“. Failure is learning, learning faster means getting ahead. When you are 10 people competing with companies of thousands, your only option is move fast or die trying.
So, how can you move fast in a difficult space, complex technology, and opinionated employees?
Here are my tips:
Don’t go overboard here, but find the quick and obvious wins.
Use a strong and opinionated code formatter, like prettier, to avoid wasting time contemplating best formatting and manually moving whitespace around, as well as debating said formatting among your team ad nauseam.
Use a CI, for instance Github Actions, to lint, type check, and build your code before you merge it, and deploy safely and reliably.
In short, find the brittle areas that type checking and other automation can’t easily guard against, and write tests for such things. Run them before you deploy code, add more when you find brittle areas, but be mindful of not overdoing it.
For a more in depth read on this topic, this post nails it
On top of that, I would also suggest taking a look at adding snapshot testing if you don’t use it already. This can be an incredibly quick and easy win to maximize stability (and visibility to unintended consequences to code updates) with very low time cost to add or maintain.
Microservices are all the rage. Serverless as well, and for good reason.
Isolating functionality means safer deploys, easier rollbacks, easier debugging, and happier teams.
At Builder, every API is a simple, isolated serverless function. Every function has its own fast and lightweight deploy.
This means we can deploy often, and safely. Made a new API? Great - deploy it. It won’t impact any other code. Improved the images API? Great - test and ship it, don’t panic it might have impact on other areas of our platform.
This has been such a revelation for our backend we even are moving our frontend to a simpler structure - by building features of our web app as isolated plugins that can deploy and rollback safely and separately.
This means we can ship faster, and fail faster, because failures are small, isolated, and quickly and easy to roll back.
Have customers asking for new features? Ship them quickly to a beta environment. At Builder we have beta.builder.io, where we can easily deploy new features that might not be ready for all our users. This means customers can opt-in to trying new things knowing that there may be bugs that they should report.
This allows quicker feedback loops and happier customers, as you are not forcing anyone into an unstable environment who doesn’t want it, but you may be surprised how many are out there who want to try new things, and don’t mind being an early tester
As engineers, we frequently love to think “I could build that!” and then indulge in a fantasy of how we would do it, in the most amazing, flawless way. The reality is, despite how we like to forget it, there is a huge difference between a prototype and a quality, usable product.
Software tools are your friend, and when it comes to optimizing team productivity, don’t forget where your priorities lie - focusing on your differentiating factors as a business, not hacking together internal tools. As a rule of thumb, if you can’t bake it in to your core product and sell it to your cutomers: don’t build it, buy it!
If you need to automate the connection of backend services, look at if Zapier can do what you need. If you need non developers to manage content of your site or app, see if a headless CMS like Builder.io can solve the problem. If you need flexible data managed by non developers but consumed by APIs, see if Airtable can handle that for you.
Overabstraction is a guilty indulgence of engineers. Why solve one problem when you can solve a broader, meatier one that could have awesome, exciting impact, and solve all future problems!
Well, I hate to be the bearer of bad news, but there’s a fatal flaw to this mentality. You may think you know exactly what people need, and how they need it, but you absolutely do not.
The only way to build standout products is to constantly find the fastest path to learning as possible. Prototype, ship small features, and get there through rapid iteration. This is how you learn precisely what your customers need, that you can (only afterward) know the proper abstraction to your solution
Put another way - it’s important to remember that abstractions are based on assumptions. Assumptions get baked into the foundation of abstraction, and most of the time our assumptions are very flawed. This is because they have not been tested, and not just the “what” of your assumption, but the “how.” These things can only be truly answered in real world testing and feedback, and rapid iteration is how you get that critical information in crystal clear terms.
If you haven’t read it, The Lean Startup is a manifesto for this approach and will push this point better than I ever can.
There’s nothing more sobering than spending months (or years) building the perfect feature with the most perfect code, making sure to handle every use case and edge case you could imagine, just to have your first customer look at your creation and bluntly say: “this is not what I need.”
Yup, it’s happened to me, way too many times. Stop thinking and get building -- the more you hyperanalyze what your customers might need in your own head, the farther you get away from what your customers actually need from your product.
Steve SewellMe: build feature to be intuitive
Users:15:45 PM - 06 Jan 2021
To be clear, don’t just go and ask your customers open ended questions about what they need. It might be a little useful for general brainstorming, but most likely you will not get the honest feedback your product needs. A better approach is to just hand a product or feature to a customer, and saying “this is ready for you to use, will you use it?” This is the only time you finally get that “yes” or “no” - either verbally or via data (usage will be there or not, no gray area).
Don’t get me wrong, you need to obsess over your customers needs. Keep the feedback coming in and the feedback loops tight. But above all else, don’t overthink it. Form a hypothesis, and get it in a customer’s hands asap. Let them tell you to what degree it solves their problems.
In startups, we have a rule of 10s. Never worry about more than 10x. When you have 1 customer only worry about getting to 10. At 10 worry about 100, and so on. Never worry past 10x -- hyperfocus on your customerbase now, and keep iterating on their needs without mental blocks and distant hypothetical “what ifs.”
tl;dr - Stop debating, start shipping. Think agile, adapt quickly, and fail faster.