Ship fast
This isn't a request for all of our product builders to feel frantic or work overtime. This is a request for everyone on our team to think about systems that enforce quick shipping. Some examples:
- How can we set up our deployment process so that we can confidently roll out new features? (Ideal scenario here is for us to roll out to product team, company, beta group, and then everyone)
- How can we roll back problems that will inevitably come from us moving quickly and shipping new product multiple times a week?
- Do we have accurate monitoring in place to let us know that things are even broken? Frontend client errors, JS performance, DB query performance, uptime monitoring, etc. *How can we cut down the amount of time it takes to deploy new features + code? If it takes longer than 5 minutes then we lose our ability to be nimble.
Most problems that arise from make changes to production are in the class of "unknown unknowns". No amount of unit testing, user testing, or edge case discovery will uncover these unknowns.
Be available after shipping something to production
It's the engineer's responsibility to go vet changes to production. Monitoring isn't perfect. Support and Sales won't catch everything right away. Customers sometimes won't catch things for weeks. The in-depth knowledge lies in the hands of the engineer.
Changes should be vetted the second that they hit production. Alerts from Vercel can be set up in Slack to alert engineers as to when a change is deployed.
Use 80/20 principle on testing
TDD can be helpful for thinking through complex systems but it also serves as a false sense of safety in many cases. Great test data is a huge lever for us. Specifically while developing locally, an engineer should have a set of test data that is as highly representative of production as possible. Common mistakes come from:
- not having enough data - UIs look great with only three items. The database query performs well with only three items. The frontend render performance is high with only three items. Please get more than three items.
- not having varied data - Long names, short names, big numbers, even numbers, odd numbers. Products that are only associated to one product line to save time in making join table records. Experienced engineers can predict what types of edge cases they need to account for in their construction of data variety.
- not having realistic data - Lorem ipsums will only get you so far. Many times you need the data to bring the application to life.
Feature flags
Having a way to roll out features to a subgroup of customers AND a quick way to toggle off a feature lends for a bit more flexibility while deploying.
Feature flags need to be monitored and cleaned up over time.
Document complex rollouts
If something takes a number of unusual steps to rollout, document so that the team can follow along quickly in the case something goes wrong.
Shape the solution
Engineers should never take a spec and just blindly execute on it. A deep understanding of the problem is required for an engineer to build a meaningful solution. The rubber meets the road as engineers write their own tickets. The breakdown of tasks is the representation of a plan and furthermore a representation of understanding the solution.
Be very wary of shiny objects
Having the opportunity to use new technologies can be very exciting. There's a time and a place for it. But it's something we try to be very deliberate about. We will opt for a battle-tested technology in almost every case before we try to be an early user for an "up and coming" tech.
Break down work, and then break it down again
Big changes are more likely have bugs. They're very hard to have code reviews on. If an engineer is working on one ticket for longer than a day then the task was not broken down enough.
Don't move the date, cut the scope
Estimation is hard. Putting a stake in the ground is necessary for people to orient in a common direction. Things always come up along the way to make things take longer. When this happens, the instinct shouldn't be to go over the promised delivery date. The instinct should be to cut scope. Get creative with taking things out.
UX, functionality, and generally how crucial something is to task success is paramount. These things should typically not be removed.
Visual design polish, auxiliary features, and perfectly scalable code are good candidates for cutting scope.
Code for others to understand
Code doesn't have to be using the latest packages, design conventions, or be endlessly component-ized to be easy for someone else to understand. Don't cut corners on the highest margin levers:
- opt for readability over being clever + concise.
- variable and function naming should be carefully considered.
- cut out all unnecessary code. Saving things for later rarely yields benefits and just creates more context to dig through.
- if you use code from examples or templates, understand what is actually needed. Get rid of the extras. Templates are helpful but they become cumbersome when you have to navigate lots of unnecessary abstractions and extra code.