Lessons Learned from Being Almost 8 Years in Startups
Overview
I recently reflected on my first job at a startup. In 2016, I joined a startup called VoxFeed, where I worked for almost a year. While VoxFeed is still an ongoing product, I doubt any of my code remains. Later, I joined Wizeline, a product and outsourcing company that grew from 60 to 300 employees in just one year. I had the opportunity to experience the famous exponential growth and some of its associated pains.
In my early days of startups, everything was great and beautiful; feeling the dumbest person in a team is a beautiful feeling; everything is just learning and having people to help you on the road. I was paid to make mistakes and ship code in the process.
Being a person who entered software by pure coincidence and had other mental models of how life worked took a big place in how I approached engineering. Coming from a music background and spending most of my pre-college life in bands, I had no idea if I was gonna succeed or not in this career. What I can tell is that I became more organized and structured than ever, and it led me to eventually become a purist in some aspects of engineering, and ultimately to just come back to simplicity.
“Taking code and processes too seriously.”
If there’s something that I enjoy about this profession, it is that there is always something new to learn, every single day. There is not only one way of doing things, and I’m one of the believers that software engineering tends to be more on the creative side than on the engineering/computer science side (interesting thread here).
Years have passed, and I look at all the things that I have learned about how to deliver effective software, and I think there are things I did wrong when it comes to coding with teams, especially in startups:
🌀 The code was more important than the final product
I did push for code that was “perfect” over making it functional. This is actually one of the statements Uncle Bob praises in his books. There was a significant price to pay when abstractions were introduced to the system (Facades, Factories, Chain of Responsibility, etc.) when the needs for the current startup were to validate a business model, sometimes code isn’t even relevant to pursue market validation.
That, of course, caused some founders to get upset about the actual velocity of the outcome, and they were right at the moment, but I fell into a Dogmatism where I believed the code was the end product, and it needed to be understandable and “read like a prose” as Uncle Bob says. It is not the right way of looking at it if the priority is to ship!
Of course, one should not write unreadable trash code, we all understand how we code is how we communicate the intentions with other developers touching a codebase, but it definitely shouldn’t be aimed to be perfect at first. If done correctly, that “we’ll revisit later” will pay off, as we would be able to ship and validate, and hopefully get more time and resources later to re-build (although the reality most of the time is it never happens 😂 but it’s better to defer some problems to the future if that means we can rely on a present)
💫 Processes were more important than delivering
I believed that teams with well-defined processes could ship faster and better code. I still believe in that. But it all depends on the maturity of a team and whether a process will shine or overshadow the final result.
The idea of having everything defined from the beginning so people can start working is not the right mindset to build a product. People should be able to build and iterate from bare not-so-well-defined ideas into a final result.
💡 The quality of the product is never put in doubt on any of the approaches; the software must work for the sake of everyone 🙏 bugs are an inevitable part of a development process, but one should aim to cover the most important cases for users to feel comfortable using your product.
Things like Continuous Integration and Continuous Delivery make teams shine at their best, as every time you are integrating code, you can show it and deliver value immediately. Pursuing these methodologies makes you stay closer to users than to technology.
Adjusting strong opinions leads to progress
➕ Writing the simplest code is your best option
Startups are like a time bomb. There isn't much time to sit down for weeks or months to contemplate building a fancy abstraction that will suffice all upcoming requirements. Instead, it's essential to focus on writing functional code that can be shipped and validated quickly, even if it's not perfect initially. This approach allows startups to move fast, make progress, and still leave room to revisit and improve the code later on.
🗨️ Just send the damn message!
Do you doubt how something should be built? How about doing a quick A/B testing with the team? Or start a conversation with another engineer to get some quick perspective?
The idea of communicating often applies even in asynchronous teams. Back at Shiphero, we used a tool called Twist, which was a thread-based communication tool to keep everyone in the company posted on any relevant updates about the company (even the CEO posted questions about how the product should behave in specific cases or what the team thought about sales strategy). I believe these habits make teams bond stronger and give clarity on why things are important, improving overall motivation and eagerness to deliver!
Another thing that makes you excel in a startup is to send damn messages on public channels. I don’t like Direct Messages because information gets lost and it’s hard to find later on. Also, you lose the ability to involve other folks in conversations.
🙏 Build momentum, and use it to refactor!
I have found that when a team has momentum (which I refer to as everyone is doing cool shit and shipping often), there is more space and room to refactor with a clearer mind.
Imagine that you wrote a not-so-polished piece of code that worked well, and then the same team inertia made you refactor that piece of code just a week later! Or, probably, someone else did it for you, and you had time to improve other areas.
Make shipping a habit
🛤️ Track your work somewhere, and learn to prioritize yourself
I lack a lot of discipline when it comes to living an organized life in general, but I have learned that I need to do things to avoid the anxiety of not doing them and blaming myself.
One of the things that has helped me a lot is to build Todo lists for the smallest things that I need to develop. Being super granular on what I need to do feels like getting small wins every certain time.
Not every To-do item ends as a ticket, but all my pending work is represented as items. To ease this process:
Have a notebook with your todo’s so you never lose sight of what needs to be done 🙏
Use timeboxing to allocate time in your calendar to get things done. Don’t need to be super strict, but at least make sure that things you put there get done in the time frame you established!
🕶️ Code Without Fear
I found this article scrolling on a Job Post from Oxide a few years ago, it’s called Fear makes you a worse programmer, and it made a lot of sense.
Trying to summarize the nice and elegant words from Julia Evans, fear makes us worse programmers because if we are afraid to move code, we are unable to make things much better than what they are. Also, fear of learning core concepts of how things work can lead us to stay in a mediocre state where we just push code but we never try to learn the fundamentals.
Also, reading Julia Evans "About” page moved me:
I have one main opinion about programming, which is that deeply understanding the underlying systems you use (the browser, the kernel, the operating system, the network layers, your database, HTTP, whatever you’re running on top of) is essential if you want to do technically innovative work and be able to solve hard problems.
It’s time to re-study my computer science course notes from college and keep learning even more! I do recommend checking this short but enlightening post and the variety of links it holds. It even references some books I have appreciated reading about Legacy Code (oh, beautiful legacy code).
One of my key takeaways is that testing your code and using version control dramatically reduces the fear of making mistakes. Leave in the comments if you have felt the relief of doing git reset
after screwing up an entire feature 🤣
Conclusions
There is no right answer to what we do in a startup, there is just an eternal process of adjustment to each team, product, and individual to perform your best job at any given point. There is no silver bullet to solve all the problems you are gonna face. What I can tell is that it’s fun as hell to be in a team that wants to change the way things work in their industry!
My next post will be: Hiring for the sake of “Growing”
Join a Startup!
We'd love to help you discover some amazing job openings that we think you'll be interested in. We're particularly excited about opportunities at startups, and while we do have a soft spot for remote jobs, there might be some great on-site positions available too. So if you're ready for a new adventure and want to work with some incredibly talented people, we've got you covered! ✨