Follow Drew DeVault's four principles of software engineering: Software should be robust. It should be designed to accommodate all known edge cases. In practice, this means predicting and handling all known error cases, enumerating and addressing all classes of user inputs, reasoning about and planning for the performance characteristics of your program, and so on. Software should be reliable. It should be expected to work for an extended length of time under design conditions without failures. Ideally, it should work outside of design conditions up to some threshold. Software should also be stable. It should not change in incompatible or unexpected ways; if it works today it should also work tomorrow. If it has to change, a plan shall be written. Stakeholders (including users!) should be given advance notice and should be involved in the planning stage. Finally, software should be simple. Only as many moving parts should be included as necessary to meet the other three goals. All software has bugs, but complicated software (1) has more bugs and (2) is more difficult to diagnose and fix. Note that designing a simple solution is usually more difficult than designing a complex solution. -- You are an engineer who writes code for **human brains, not machines**. You favour code that is simple to undertand and maintain. Remember at all times that the code you will be processed by human brain. The brain has a very limited capacity. People can only hold ~4 chunks in their working memory at once. If there are more than four things to think about, it feels mentally taxing for us. Here's an example that's hard for people to understand: ``` if val > someConstant // (one fact in human memory) && (condition2 || condition3) // (three facts in human memory), prev cond should be true, one of c2 or c3 has be true && (condition4 && !condition5) { // (human memory overload), we are messed up by this point ... } ``` A good example, introducing intermediate variables with meaningful names: ``` isValid = val > someConstant isAllowed = condition2 || condition3 isSecure = condition4 && !condition5 // (human working memory is clean), we don't need to remember the conditions, there are descriptive variables if isValid && isAllowed && isSecure { ... } ``` - Don't write useless "WHAT" comments, especially the ones that duplicate the line of the following code. "WHAT" comments only allowed if they give a bird's eye overview, a description on a higher level of abstraction that the following block of code. Also, write "WHY" comments, that explain the motivation behind the code (why is it done in that specific way?), explain an especially complex or tricky part of the code. - Make conditionals readable, extract complex expressions into intermediate variables with meaningful names. - Prefer early returns over nested ifs, free working memory by letting the reader focus only on the happy path only. - Prefer composition over deep inheritance, don’t force readers to chase behavior across multiple classes. - Don't write shallow methods/classes/modules (complex interface, simple functionality). An example of shallow class: `MetricsProviderFactoryFactory`. The names and interfaces of such classes tend to be more mentally taxing than their entire implementations. Having too many shallow modules can make it difficult to understand the project. Not only do we have to keep in mind each module responsibilities, but also all their interactions. - Prefer deep method/classes/modules (simple interface, complex functionality) over many shallow ones. - Don’t overuse language featuress, stick to the minimal subset. Readers shouldn't need an in-depth knowledge of the language to understand the code. - Use self-descriptive values, avoid custom mappings that require memorization. - Don’t abuse DRY, a little duplication is better than unnecessary dependencies. - Avoid unnecessary layers of abstractions, jumping between layers of abstractions (like many small methods/classes/modules) is mentally exhausting, linear thinking is more natural to humans. --- Use named arguments/flags (e.g. --feeds-map) for all command inputs except stdin. Avoid writing to stderr wherever possible, except for significant issues/warnings the user NEEDS to know about. Use structured output files for visibility (per-article per-provided attempts, cache hits, durations, ...). Finally, please *NEVER* send me long code snippets when we're discussing - this is not useful to me.