These principles are not meant as hard, fast rules, but are rather just here to provide some guidance when you find yourself asking whether a path is worth taking.
- Understandability over Performance: We would rather a human be able to easily understand code rather than it being maximized for performance. (Exceptions exist, but should be noted when doing so)
- Nimbleness over Early Delivery: We would rather code be clean, small, single-purposed, modular, and well-named rather than being delivered in the shortest amount of time. Refactor before presenting for Code Review. This may also be thought of as "high standards".
- Don't Repeat Yourself (and help others not to as well): We have lots of "common" style libraries for a reason, and you should use them, but don't go crazy on abstracting everything you create, if you think there is a 80% or greater chance that someone will eventually use something you are creating, abstract it into an appropriate lib right now. Otherwise, leave it alone.
- Progressiveness over Stability: This has many other names, including Move Fast and Break Stuff & Refactor Ruthlessly. When in doubt, embrace change over ensuring that the minimum amount of things break with each merged Pull Request. This includes, but is not limited to, package/dependency updates.
- The key here is that mistakes are expected and should be embraced as a tool for learning (though we should fix when they occur as quickly as possible). Do not be afraid to merge just because something is "Big". Do not be afraid to refactor just because something is "critical" or "hasn't been touched in a long while".
- We should strive to improve processes to minimize mistakes, but one of those tactics should never be "do not touch".
- Automation over Repetition: We land very strongly on the side of the fence of "take the extra time to automate it". If you are performing an action more than twice, explore with others if the action is able to be and worth automating.
When in doubt, create a JSDoc (or equivalent). Just do it. These show up in Typescript helper boxes, are automatically added to the autogenerated documentation, and are just generally helpful for a future developer with fresh eyes (and in six months, that might be you).
Less helpful are comments inside the code itself. If you find yourself writing one of these, ask hard if you should instead extract that logic into a function that is named after what you were going to write in the comment. Still need more clarification? Write a JSDoc for the new function you just made.
Sustainability Engineering is the act of attacking technical debt. This is an act that can be never ending, so how much is the right amount?
We set aside 20% of time for Sustainability Engineering. If you find a certain area requires much more than this, figure out why.
Did we allow code to be deployed without proper review? If so, let's fix it and put something in place so this doesn't happen again.
Are you just being overly pedantic? (perfectionist, but not a good thing) Let's ask ourselves a few questions:
- Am I feeling this way because I am worried that we will have to do a full rewrite of functionality someday in the future?
- If yes, feel free to create the ticket and ignore other questions.
- Am I feeling this way because the way the functionality is written is difficult to understand?
- If yes, ask exactly how difficult it is. If it is simply an ESLint rule, and borderline, let it go. If you were reading it, and it seemed like Japanese, create the ticket (especially if you are the original author).
- Am I feeling this way because there is a high likelihood of reuse?
- If yes, do NOT create the ticket, and build this into the project that will reuse it instead.
- Do I really just want to rewrite it because it sounds fun or a learning experience to do so?
- Ask a more Senior Engineer if they think the theory holds. If yes or maybe, feel free to use your learning time on this.
In general, when in doubt, write unit tests. For most functionality, Test-Driven-Development is like a way to get these tests for free. Even if you aren't writing tests, code must be written as though tests are present (testable code). More details:
- If you are creating a Data Model, and the model has an in-memory database, definitely write tests. This is not only extremely helpful from an assurance perspective, but also important as Models form the foundation for most functionality.
- If you are creating a Data Model and there is no in-memory database, the benefit of tests are fairly limited, and should be assessed whether they are worth it.
- If you are creating an API mutation, resolver, route, etc, write a test.
- If this route has only one action and that action is tested, OK, maybe you don't. But be sure that your use case is tested in the root function.
- If you are creating a Job, test it.
- Seriously, test it. Just do it. These are pretty easy to test.
- If you are creating a frontend component or hook:
- Do not test styles. Maybe test disabled and visibility, but maybe not (many forms of this). These areas are likely to cause very flakey tests.
- If this is used in more than one place, test it.
- If this is presented to an end consumer, probably test it, but not always. Determine by criticality and frequency of use.
- If this is a single use and the only consumers are people who work for us, you probably do not need to test it, but we may want to anyway.
As we mature as a platform, we will have more and more of these in place, but to start with, these will be exceedingly rare.
This is generally fairly ineffective, especially when done by the same person who created the code. This is entirely optional in most cases, but if there are no unit tests to cover your functionality, ensure this is done by someone.
This should also be done as a protective measure when going from staging to prod.