Our (my) Engineering Philosophies
Hello world, it's been a while! Unfortunately, this still doesn't mean I'm back to blogging regularly. I'm doing this because Apple News is complaining that I haven't posted in a while and they threatened to take my site down from their app.
I still don't really have the bandwidth to blog (it's 3:01 am as I'm writing this) because I've still been really busy working on my startup. But I've written a lot of documentation, notes, and wiki's for my team, so here's a rehash of one of my wiki posts.
Introducing, our engineering philosophies!
We believe every engineer should be a full-stack engineer and a knowledge expert of a particular area. We've had countless experiences where top engineers are only experts at a particular technical area, which lead to poor design decisions because of a lack of understanding of the bigger picture.
So at [our company], we want every engineer to be responsible for any or all parts of the technical stack. Not only this, we believe every engineer is a product owner - you are responsible for owning features end-to-end. This doesn't mean you are responsible for every piece of the code, but you are responsible for seeing the product to completion. This will result in more developer empathy, informed decision making, and sharpen all your skills; creating a better product for your users.
We believe in maintaining a high standard of code quality and engineering practices. Here are some general philosophies that we try to practice/preach every day:
Build for today, but enable for tomorrow
- We're all in this together, no one is an enemy, help each other out!
- There is no such thing as a dumb question. It doesn't feel good when researching an answer takes two days, but someone could have answered it in a few minutes. Just ask!
- Spend time to whiteboard, design, and architect before writing any code. Think about the problem and all use cases before writing code that can be hard to change later.
- Document frequently (i.e. code documentation and wiki documentation).
- Peer review code before merging a pull request
- Review any negative effects of bringing in a dependency (does it bring in additional unnecessary dependencies? What is its file size? License? Is it actively maintained? etc.)
- Follow the style guides and rules when writing your code. The more often you write your code the right way, the less mistakes you'll make in the long term.
- Test for edge cases often, try to make your code break.
- Design for the user, not for the engineer.
- Code readability over code optimization; your code should feel like you're reading comments rather than reading code.
- Continuously learn, break shit, fix stuff, and look for how to make things better.
- Stay informed, read the news, emerging code trends, etc. Ask your peers on their favorite learning resources!
- Experiment often, but stay pragmatic.
- Have fun!
It is the duty of every engineer to keep everyone honest and keep code standards high
Almost all of our projects follow the git-flow strategy, as defined here and here. We chose this methodology due to the limitations of our CI/CD pipeline through CircleCI. A better strategy can be figured out later.
So if you are developing a new feature locally, either work on a fork or create a new branch on the repo. Then when you are ready to merge into develop, submit a pull request. When we are ready to deploy to production, we'll simply rebase it onto master (or perform another pull request).
Writing documentation is necessary to ensure anyone can pick up work that you may have left off. When writing documentation, we like to try to keep documentation as close to the source code as possible. However, this can cause the unintended issue of bloating our projects/code with unnecessary docs, so we like to apply the following practices:
- When writing code comments, document the why, not the how. It is not necessary to document how your code block works line by line, but why it needs to be done and what it is achieving.
- Github README's should provide just a general overview on how the project works, any gotchas, setup instructions, important commands, etc.
- Any other important notes on the architecture, general philosophies, integrations, and etc. should be provided in [this company wiki]
- Document all third party services in [the company wiki]. Integrations with other services is very domain-specific, so help others get up to speed with any quirks with integration.
Performance is of utmost importance to improve the user experience. With that said, at the current scale we operate there is no need to be the most performant. Prioritize readability and development speed over optimizations. Our focus is on trying to follow good performance practices, but not spend weeks just to shave 100ms off an API request. Our general rules of thumb are:
- API requests/responses should be less than 500ms, < 200ms is the ideal speed
- Try to keep JS bundles smaller than 200kb
- Code split every front-end UI route
- Ensure we only bring in the necessary dependencies in our modules
- Regularly do a bundle analysis to measure the size of our bundles
- Anything that can be run async, should be run async
Kelvin - "You may be shocked with our high 500ms threshold, but we're a small team with a small budget, we can't afford that fancy-ass cacheing layer and don't have the bandwidth to roll our own yet"
We believe testing is important, but it has unfortunately fallen on the wayside and has not been a critical path for us . We are in the process of reviewing how to better implement tests into our platform and our first attempt should do the following:
- Unit test and integration test for API services.
- Do not unit test UI code, provide end to end integration tests instead
- Do not write tests for third party code (it's their responsibility to ensure their code is written properly and we can assume that it works as intended)
Kelvin - "Don't be shocked that we don't have tests yet. BUT as of 12/15/2019, we do have the e2e test infrastructure set up."
Maintaining code quality
Code cleanliness is extremely important to provide uniformity across our code base. Here's what we do to enforce it.
We use an
.editorconfig to ensure there is uniformity in all of our code environments
We use ESLint for lint rules. We do not allow unlinted code to be checked in (we actually run a lint check before submitting any code).
Kelvin - "That's it for now! I will continue to update this as the team gets larger and we develop more guidelines.