Guidelines for developing software and reverse engineering
The following is a list of guidelines that I try to follow when developing software to make projects more sustainable in the long term. Ideally, following the guidelines will prevent major structural issues later on.
These are guidelines, not rules, and as such some of them are expected to be violated at some point, though doing so comes with major downsides.
- Source control helps to track down when bugs were introduced, keep a record of old good versions of code, etc
- most source control is not optimized for binaries, so every time you commit a new version of the same binary to source control, the size of the repo increases by the size of the binary
- committing binaries to source control causes the amount of time it takes to pull your repo to increase. this slows things down for devs and for CI/CD
- binaries committed to source control can be abused to try to stealthily introduce backdoors, as was the case with the xz_utils backdoor
- use something like
git-lfs, which optimizes storage - avoid committing different versions of the same binary
- all of the same reasons as not committing binaries to source control, plus you know you will be changing these items frequently.
- Untested code is frequently broken code.
- Untestable code is code that should be assumed to be riddled with bugs
- If you can't test a component without testing the whole product, you probably need a new test
- any commits to a repo should not break the build, as it causes issues for devs that pull while the build is broken
- ci/cd ensures that you know if something will break the build as soon as it happens
- automated regression tests help to identify when you have broken some existing functionality
- this enables new developers to start up with development immediately
Thou shall enable build reproducibility by documenting build toolchain info, OS, compiler, build dependencies, etc. in in-tree documentation
- This is a small and easy thing to do that will help new developers
- Your devs shouldn't have to fight your code to be able to test a bug fix
- Your users should be able to build and use your code without having to read it first
- see https://github.com/google/eng-practices for examples of good practices
- If you need to extract symbols or something for a new version of a product, it needs to be documented.
- Not doing this just wastes time and effort
Thou shall strive to limit the size of merge requests so that a merge request does not take more than a maximum of one day to review
- Exceedingly large code reviews waste time and are a little bit discrespectful of your code reviewers' time
Thou shall create issues or user stories in some sort of tracking system as soon as you are aware that they need to be done
- Tracking issues/user-stories in your head is not reasonable
- At the end of the day your code needs to be readable, understandable, and modifiable by normal humans
Thou shall write code defensively; assuming it will be copied and incorrectly used by a future developer
- If you don't know something and can't google it, ask a question to a dev who knows. Then document it.
Thou shall include information about reverse engineered structures and where to look to review their usage or re-reverse them in code comments
- If a structure needs to be re-reversed in a different version or if it was so fundamentally incorrect that it needs to be re-reversed from scratch, this will make everyone's lives easier
- Multiple main branches probably means you are maintaining duplicate copies of the same source code
Thou shall not fork internally maintained repos unless original code cannot be maintained or the new commits are undesired in the original repo
- Every time you fork a repo you ensure that code has to be committed to two places when fixing a bug in both repos
- ensure that you at least attempt to commit to the existing repo
- Both will likely change
- If your complier says a branch cannot ever be reached or that there is a buffer overflow, you should resove that