Design (& Prototyping)
- Prototype (POC) instead of throwing in all resources to the first design that comes to your mind.
- Resist the temptation of quick but dirty solution (hacks / workarounds / patches). Think about the cost of maintenance and knowledge transfer in the future.
- Research before building house-made solutions / tools. It is very likely that a common problem already has a mature solution or can be handled by an opensource library. Don't reinvent the wheel unless you have a good reason.
- Keep documentation / ReadMe along development when working on a large project. It can save you from numerous meetings on progress tracking / tutorials / handover.
- Scalable and open architecture is preferable. Think about how difficult it will be to adapt your design to requirement & technology changes.
- Make human-friendly software. Allow the users of your software to break the glass in case of emergency.
Coding
- Keep your codes clean (unless you are developing one-time throw-away codes):
- Use meaningful function and variable names.
- Break down large trunk of codes into smaller functions.
- Write self-explaining codes and minimize comments.
- Dumb-but-straightforward over smart-but-hard-to-understand / maintain.
- Logging is essential for runtime debugging. Produce user-friendly error reports.
- Always write unit-tests for your codes. Testing & maintenance should not be an afterthought. Don't write untestable / unmaintainable codes.
Cleanup & Prepare for code review
- Why you need code review is equivalent to why an author need an editor. (Thus, the reviewer need know the high-level paradigm to do good editor's work in this domain.)
- Go through your codes with an editor's hat on before sending them to someone else:
- Architecture
- Design the solution with natural logic flow rather than twisting for a "smart" shortcut.
- Balance between too much restrictions and too much workaround to support everything.
- Be careful with too aggressive solution that overkill.
- Use an external library / framework or not: weigh between "reinventing the wheel" and "using an ill-fitted wheel"
- Abstraction
- Modularize functionalities for maintenance and reusability
- Look out for duplication and repetitive codes.
- Code smells
- Too complicated: is there a shorter way? Can it be broken down? Using an external library?
- Complex algorithm (especially recursive): is there any logic bugs?
- Readability
- Audience (Platform/framework codes vs. codes running on a platform/using a framework)
- If budget is tightly constrained, prioritize the quality of platform/framework codes as they will be used much more frequently.
- When writing platform/framework codes, keep two types of audience in mind:
- For other developers also developing this platform/framwork:
- Clean code is a must.
- Be careful / selective with any new feature to add ("Do we really need it at the cost of more complexity?")
- Docs to explain the architecture.
- If the structure cannot be optimized, at least make the codes error-prone by comprehensive unit testing.
- Platform need to be able to test itself throughout. Never rely on projects running on the platform to QA it.
- For developers writing codes to run on this platform/using this framework:
- Docs to explain how to use the platform as an end-user, e.g. API docs.
Commit to codebase
- Always have code review before committing / merging feature branch to master.
- Keep commit history clean. A project should follow the same flow, e.g. GitFlow ( https://guides.github.com/introduction/flow/ ).
- When developing new features or bugfixing, work from a feature branch and create merge request (MR) once it is ready.
- Write meaningful commit / MR titles & messages so that others can quickly get an overview without reading through the code diff.
- Commit / MR title needs to tell what is done at a glance so that others can stroll down the git history and have an overview without clicking into the commit message or codes. That helps others to narrow down which commits they might need to look into. For example, "small change to 2 holiday names" is better than "small change".
- Then, a good commit / MR message explains how you did it in a few lines so that most of the time others don't have to read the code diff which is time & energy consuming.
- If the commits in a feature branch look messy (lots of experimenting and reverting), use the "Squash commits" option in MR so that there will be only one commit shown in master after merging.
Automation & CI/CD
- Streamline the process and minimize manual steps in it.
- You have more time to focus on real development.
- More integration / deployment cycles available during the same period of time means more chances to discover & fix a bug at an early stage (the later to discover, the more expensive to fix).
- Create CI/CD pipelines with automate testing (unit / integration / system) that safeguards the codebase and the runtime environment.