From 7563634f53bfcd503d812468427055d2649ef69f Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Sun, 11 Dec 2022 14:55:29 -0800 Subject: [PATCH] Add coding guide --- Coding Guide.md | 353 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 353 insertions(+) create mode 100644 Coding Guide.md diff --git a/Coding Guide.md b/Coding Guide.md new file mode 100644 index 00000000..51e804d9 --- /dev/null +++ b/Coding Guide.md @@ -0,0 +1,353 @@ +# Coding Guide + +This document serves as the official code standards guide for `BurnOutSharp` internal development. Please note that this is a work in progress and may not encapsulate all standards expected of new or existing code. + +## General Code Guidelines + +This section contains information on code standards regardless of which part of the project you are working in. + +### Style and Naming + +- Prefer `System` namespaces for supporting operations before external ones. + +- Ordering of `using` statements goes: + - `using System.*` + - `using ` + - `using static ` + - `using X = Y` + +- Use 4 spaces for `tab`. + +- Curly braces should generally start on the line after but inline with the start of the previous statement, even if multiline. + + ``` + if (flag) + { + DoSomething(); + } + else if (flag2 + && flag3) + { + DoSomething2(); + } + ``` + +- Multi-line statements need to have following lines indented by one step at minimum. + + ``` + if (flag) + { + DoSomething(); + } + else if (flag2 + && flag3 + && (flag4 + || flag5)) + { + DoSomething2(); + } + ``` + +- Methods and classes should use `PascalCase` for naming, even `internal` and `private` ones. + +- Class properties should use `PascalCase` for naming, even `protected` and `internal` ones. + +- Instance variables should use `camelCase` with a `_` prefix for naming, even `protected`, `internal`, and `private` ones. + +- In-method variables should use `camelCase` without a `_` prefix for naming. + +- Include explicit access modifiers for all class-level properties, variables, and methods. + +- Avoid making everything `public`; only include the necessary level of access. + +- Avoid making every method and class instance-based. Use `static` if your method does not need to access instance variables. Use `static` if your class only contains extensions or methods used by other classes. + +- Null-coalescing and null-checking operators can be used to make more readable statements and better get across what a statement or string of statements is doing. + + ``` + if (obj?.Parameter != null) { ... } + + bool value = DoSomething() ?? false; + ``` + +- `#region` tags, including nested ones, can be used to both segment methods within a class and statements within a method. Indentation follows the surrounding code. + + ``` + #region This is the first region + + public static void Method() + { + #region This is an in-code region + + DoSomething(); + + #endregion + + DoSomething2(); + } + + #endregion + ``` + +- Try to avoid use of other preprocessor directives unless consulting ahead of time with the maintainers. + +- Interfaces should be listed in alphabetical order + + ``` + public class Example : IBindable, IComparable, IEquatable + ``` + +- Use the `` tag when possible to avoid out-of-date information. + + ``` + public interface IInterface + { + /// + /// Summary to inherit + /// + void DoSomething(); + } + + public class Example : IInterface + { + /// + public void DoSomething() { ... } + } + ``` + +### Methods + +- Try to avoid including too much duplicate code across methods and classes. If you have duplicate code that spans more than ~5 lines, consider writing a helper method. + +- Try to use expressive naming. e.g. use names like `PrintSectionTitles` and not `DoTheThing`. + +- Try to avoid having too many parameters in a method signature. More parameters means more things interacting. + +- Use method overloading to avoid unnecessary complexity in a single method. + + ``` + Instead of: + + Print(string idString, byte[] idArray, int idInt) { ... } + + You should: + + Print(string id) { ... } + + Print(byte[] id) { ... } + + Print(int id) { ... } + ``` + +- Use optional parameters when the default value is the most common. + + ``` + Print(string id, bool toLower = false) { ... } + ``` + +### `if-else` and `switch` statement syntax + +- If all statements in the block are single-line, do not include curly braces. + + ``` + if (flag) + DoSomething(); + else if (flag2) + DoSomething2(); + else + DoSomethingElse(); + ``` + +- If any of the statements is multi-line _or_ the `if-else` statement is multi-line, include curly braces. + + ``` + if (flag) + { + DoSomething(); + } + else if (flag2 + && flag3 + && flag4) + { + DoSomething2(); + } + else + { + DoSomethingElse(); + DoSomethingEvenMore(); + } + ``` + +- If comparing against values, try to use a `switch` statement instead. + + ``` + As an if-else statement: + + if (value == 1) + DoValue1(); + else if (value == 2) + DoValue2(); + else if (value == 3) + DoValue3(); + else + DoValueDefault(); + + As a switch statement: + + switch (value) + { + case 1: + DoValue1(); + break; + case 2: + DoValue2(); + break; + case 3: + DoValue3(); + break; + default: + DoValueDefault(); + break; + } + ``` + +- When using a `switch` statement, if all switch cases are single-expression, they can be written in-line. You can also add newlines between cases for segmentation or clarity.If the expressions are too complex, they should not be. + + ``` + switch (value) + { + case 1: DoValue1(); break; + case 2: DoValue2(); break; + case 3: DoValue3(); break; + + default: DoValueDefault(); break; + } + ``` + +- If any of the switch cases are multi-expression, write all on separate lines. You can also add newlines between cases for segmentation or clarity. + + ``` + switch (value) + { + case 1: + DoValue1(); + break; + case 2: + DoValue2(); + break; + case 3: + DoValue3(); + break; + + default: + DoValueDefault(); + DoValueAsWell(); + break; + } + ``` + +### Commenting + +- All classes and methods should contain a `summary` block at bare minimum to explain the purpose. For methods, it is highly recommended to also include `param` tags for each parameter and a `return` tag if the method returns a value. Do not hesitate to use `remarks` as well to include additional information. + + ``` + /// + /// This class is an example + /// + /// + /// This class does nothing but it is useful to demonstrate + /// coding standards. + /// + public class Example + { + /// + /// This property is the name of the thing + /// + public string Name { get; private set; } + + /// + /// This method is an example method + /// + /// Indicates if the value should be printed + /// A value between 1 and 10, or null on error + public static int? PrintAndReturn(bool shouldPrint) + { + ... + } + } + ``` + +- In-code comments should use the `//` syntax and not the `/* */` syntax, even for multiple lines. + + ``` + // This code block does something important + var x = SetXFromInputs(y, z); + + // This code block does something really, + // really, really, really important and + // I need multiple lines to say so + var w = SetWFromInputs(x, y, z); + ``` + +- Comments should be expressive and fully explain what is being described. Try to avoid using slang, "pointed comments" such as "you should" or "we do". + +- If comments include links, they can either be included as-is or using the `` tag + + ``` + // This information can be found from the following site: + // + ``` + +- Try to avoid using multiple, distinct comment blocks next to each other. + + ``` + // We want to try to avoid this situation where + // we have multiple things to say. + + // Here, the statements are not inherently linked + // but still need to go in the same area. + // + // But here the statements are logically linked but + // needed additional formatting + ``` + +## Project and Class Organization + +This section contains information on project and class organization principles that depend on the part of the project you are working in. See the following table for details. + +| Project | Description | +| --- | --- | +| `BurnOutSharp` | One file per class. See below for details on subdirectories. | +| `BurnOutSharp/External` | One directory per external project. | +| `BurnOutSharp/FileType` | One file per file type. | +| `BurnOutSharp/Interfaces` | One file per interface. | +| `BurnOutSharp/PackerType` | At least one file per packer type. Partial classes allowed. | +| `BurnOutSharp/ProtectionType` | At least one file per protection type. Partial classes allowed. | +| `BurnOutSharp/Tools` | Two files - one for extension methods and one for utilities. | +| `BurnOutSharp.Builder` | One file per executable type. | +| `BurnOutSharp.Matching` | Flat directory structure. Include interfaces and base classes. | +| `BurnOutSharp.Models` | One directory per executable type. One file per object model. | +| `BurnOutSharp.Wrappers` | One file per executable type. Common functionality goes in `WrapperBase.cs`. | +| `Test` | All functionality lives in `Program.cs`. | + +If the project or directory you are looking for is not included in the above, please consider it to be outside the context of this document. + +## Code Organization + +This section contains information on in-code organization principles that depend on the part of the project you are working in. See the following table for details. + +| Project | Description | +| --- | --- | +| `BurnOutSharp` | Varies from file to file. | +| `BurnOutSharp/FileType` | `Scan(Scanner, string)`, `Scan(Scanner, Stream, string)`, helper methods. | +| `BurnOutSharp/Interfaces` | Methods ordered alphabetically. | +| `BurnOutSharp/PackerType` | `IContentCheck` implementations, `INewExecutableCheck` implementations, `IPortableExecutableCheck` implementations, `IPathCheck` implementations, `IScannable` implementations, helper methods. | +| `BurnOutSharp/ProtectionType` | `IContentCheck` implementations, `INewExecutableCheck` implementations, `IPortableExecutableCheck` implementations, `IPathCheck` implementations, `IScannable` implementations, helper methods.. | +| `BurnOutSharp/Tools` | Methods grouped by function. Regions ordered alphabetically. | +| `BurnOutSharp.Builder` | Two copies of each non-generic method: one for byte arrays and one for Streams. | +| `BurnOutSharp.Matching` | Varies from file to file.. | +| `BurnOutSharp.Models` | No methods at all, just properties. | +| `BurnOutSharp.Wrappers` | Follow region and method grouping from existing wrappers. | +| `Test` | New functionality should be added as a combination of a flag with a long and a short form, a new line in the help text, and a new method (if necessary). | + +If the project or directory you are looking for is not included in the above, please consider it to be outside the context of this document. \ No newline at end of file