Software Development Principles
- Keep it short and simple (abbreviated KISS)
- Boy scout rule: Leave the code cleaner than you found. This helps for incremental codebase cleanup.
- Don't repeat yourself (abbreviated DRY): Every piece of knowledge (information or behavior) must have a single representation in the system.
- There's more than one way to do it (abbreviated TMTOWTDI and pronounced "Tim Toady"): Design the system to be flexible: a user may find multiple ways to achieve his goad. Used in Perl, avoided in Python.
- You aren't gonna need it (abbreviated YAGNI): Implement functionality only when you need it.
- Open-close: An entity (function, class, module) should be open to extensions, but close for modifications.
- Use intention-revealing names.
- Avoid disinformation.
accounts_list(linked to the implementation, namely an iterable of type
- Make meaningful distinction between variables with close names.
money_amountare the same when used in the same context.
- There is no difference between
ProductInfodue to the noise words used as suffixes.
- Use pronounceable names.
- Use searchable names.
- Too generic search when using
7. A constant named
- Avoid encodings, mental mapping, prefixes, and type information.
multiplication_factor, not simply
- Avoid abstract words such as:
- Functions should have verbs in their names.
- Don't be cute or use puns.
- Make the names context-specific
zipcodein a context where
destination_addresscan refer to any of them. Transform it into
- Use code to document the code.
if (employee.has_flag(HOURLY) && employee.age > 65) // check if the employee is eligible for full benefits
- Use good comments:
- Legal, for example copyrights
- Description of a decision
- Avoid bad comments:
- Dependent information, such as constants values and parameters names that could be changed in future implementations
- Commented-out code
- Just remove it, you have
gitto restore it if you need to.
- Just remove it, you have
- Journal comments (use
- Position markers, such as
// Public methods
- Obvious comments, such as
// Defaults constructor
Lines of Code
- Declare variables close to their first usage.
- Avoid multiple
switchstatements. The only ones should occur when creating a class in a factory. Multiples switches are a sign of bad design. Inheritance can be used.
- Keep lines short (120 characters per line recommended).
- Use implicit line joining for splitting long lines of text.
- Do one thing.
save_page_to_filecalls multiple functions to: download page and write the content to a file. It does not execute this whole functionality on its own.
- Only one level of abstraction per function
- A function
get_links_from_html_pagedownloads the page and calls another function to search the effective text with a RegEx (which is at another abstraction level).
- Do only what's expected.
check_passwordfunction will not initialize a session.
- Small functions, with 20 LoCs (recommended)
- Only one indentation level
- Use structured programming: one entry, one exit.
- Remove functions that are not called.
- Prefer fewer arguments. When over 3 arguments, maybe you can pass a class instead.
- Avoid output arguments.
- No flag arguments. Split the function into multiple functions, one per each flag possible value.
- Don't pass
Noneto any argument.
- Annotate their type.
- Do not return
- Do not return error codes. Raise exception instead.
- Annotate its type.
- Document only the functionalities that are on the edge of the system you design. The others impose friction during implementation.
Objects and Data Structures
- Use classes for allowing access to members only through methods. This allows further processing and verifications.
- Use data structures to group data. The processing is done through another functions.
- Avoid hybrids (half class, half data structure), namely classes with exposed members.
- Single Responsibility Principle (abbreviated SRP) or High Cohesion: A class should have one and only one reason to change.
- Low coupling: A class should have the least possible dependencies.
- Dependency injection: Delegate secondary responsibilities to another objects that are dedicated for that purpose.
- Do not manage the profile picture in a
Userclass, just create a new
ProfilePictureone that manages itself.
- Composition over Inheritance (abbreviated CoI): Prefer embedding of different objects within another object (according to the dependency injection principle) instead of inheritance. The latter will expose all the public methods of the parent class and is limited by some programming languages (for example, multiple inheritance could not be achieved).
- Interface Segregation Principle (abbreviated ISP): A client should not have access to methods it doesn't use. If a broader interface is used, another interface (called role interface) can be created to limit the exposed methods.
- Liskov Substitution Principle (abbreviated LSP): If a class is substituted with any of its subclasses, the program should work well. This implies that the return types and exceptions types remains the same. In addition, no side effect is introduced.
- Declare instance variables at the top of the class.
- After the instance variable, place each public function, eventually followed by the private methods it calls.
- Law of Demeter: A method should not know about the implementation of an object it manipulates.
- If a method does not use the parent object, transform it into a
- Annotate types in class beginning.
- Initialize the members in constructor.
- Place a
_in front of the name if the member is protected (that can be used in a subclass) and
__if it is private (used only in the current class).
- Use only absolute imports.
- Import only the functionalities that are exposed explicitly by the module (for example, the ones in
__init__.py). Do not navigate the inwards of the module.
Source Code Files (or Modules)
- Avoid huge files (over some hundreds LoCs).
- Place the called functions after the callee. The code can be read as a newspaper.
- Add a docstring describing the source code functionality.
- Maintain a predictable structure:
- Type annotations aliases
- Module-level functions
- Hide third party code with wrapper classes.
- Translate learning code in unit tests. If something changes in the future versions, the tests will fail.
- Implement interfaces and dummy classes for upcoming functionalities.
- Separate the startup process of the application.
- Establish formatting rules with your team. Implement them using linters and formatters.
- Avoid formatting your code manually.
- Use exceptions, not return codes.
- Define a root exception and inherit from it for each child exception.
- Provide context to an exception with the help of docstrings and name uniqueness.
- Push error handling to the edge of each component of the system.
- Create a test for each state of a function/object.
- Test an exception in a function separately, not in the test targeting the normal functioning
- Keep only one
- FIRST: The tests should be:
- Self-validating by returning a boolean indicating if it passed or not
- Written before the code that make them pass (in a TDD fashion).
- Name each test
- Keep the production-grade standards for test code.
- The tests can be place into a different
testsfolder or in the same implementation file.
- Run all the tests.
- Use coverage to determine code that is not tested.
- Convention over configuration (abbreviated CoC): Develop software by establishing "sensible defaults" wrapped into conventions. This will ease the usage compared to the more flexible approach of configuration.
- Expose the user-configurable aspects via a separate configuration class that parse a configuration file to get its members values.
- Expose the aspects configurable by programmers via constants in the current file or in a separate module, only with configuration constants.