Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Code comments standard #298

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
317 changes: 317 additions & 0 deletions docs/standards/code-comments.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,317 @@
---
layout: standard
order: 1
title: Code Comments
date: 2023-10-18
id: SEGAS-00012
deanwhitehouseHO marked this conversation as resolved.
Show resolved Hide resolved
tags:
- Documentation
- Ways of working
related:
sections:
- title: Related links
items:
- text: Minimal documentation set for a product
href: /standards/minimal-documentation-set-for-a-product/
---

These Code Commenting Guidelines are designed to ensure that code is documented at a minimum standard, making it easy for new engineers to quickly get up to speed and preventing the isolation of knowledge within the team. By following these guidelines, you help streamline the onboarding process for new team members, ensuring that code is accessible and understandable.

This standard is essential for programmers, as it promotes effective communication and knowledge sharing within the team, ultimately contributing to the overall success of the project.
deanwhitehouseHO marked this conversation as resolved.
Show resolved Hide resolved

---

## Requirement(s)

- [Comments MUST be strategically placed to enhance code comprehension and maintainability.](#comments-must-be-strategically-placed-to-enhance-code-comprehension-and-maintainability)
- [Comments MUST be explanatory, not merely descriptive, and use concise, fully-formed sentences.](#comments-must-be-explanatory-not-merely-descriptive-and-use-concise-fully-formed-sentences)
- [Comments MUST add value to the understanding and readability of the code.](#comments-must-add-value-to-the-understanding-and-readability-of-the-code)
- [Comments MUST adhere to a documented standard, such as Docblock, and include all necessary information.](#comments-must-adhere-to-a-documented-standard-such-as-docblock-and-include-all-necessary-information)
- [Comments MUST use neutral, unopinionated language and remain applicable and relevant after a code refactor.](#comments-must-use-neutral-unopinionated-language-and-remain-applicable-and-relevant-after-a-code-refactor)
- [Comments MUST NOT be present in production deployments.](#comments-must-not-be-present-in-production-deployments)
- [Comments MUST NOT be duplicated or be used as a substitute for deleting code.](#comments-must-not-be-duplicated-or-be-used-as-a-substitute-for-deleting-code)
- [Comments MUST NOT contain sensitive data, such as API keys, tokens, etc.](#comments-must-not-contain-sensitive-data-such-as-api-keys-tokens-etc)

### Comments MUST be strategically placed to enhance code comprehension and maintainability.
deanwhitehouseHO marked this conversation as resolved.
Show resolved Hide resolved

The placement of comments is essential for code comprehensibility and maintainability. Comments should be thoughtfully positioned to explain complex logic, significant decision points, or any non-trivial functionality.

Positioning comments at the top of files, structural elements, or before specific code sections assists both newcomers and existing team members in understanding the code's purpose. This fosters a collaborative environment, ensuring seamless knowledge transfer and facilitating effective contributions from colleagues to the project.
deanwhitehouseHO marked this conversation as resolved.
Show resolved Hide resolved

#### Positive Example
```
"""
This function calculates the total price of items in the shopping cart.

:param cart: The shopping cart as a list of items.
:return: The total price as a float.
"""
def calculate_total_price(cart):
total_price = 0.0
for item in cart:
total_price += item.price
return total_price
```

#### Negative Example
```
def calculate_total_price(cart):
# This function calculates the total price
total_price = 0.0
for item in cart:
total_price += item.price
return total_price
```
deanwhitehouseHO marked this conversation as resolved.
Show resolved Hide resolved

### Comments MUST be explanatory, not merely descriptive, and use concise, fully-formed sentences.

The use of language in your comments is essential for effective communication. Comments should serve the purpose of explaining the reasoning behind the code rather than solely describing its functionality. They should be clear, concise, and composed of fully-formed sentences.

This practice ensures that readers can grasp the code's intent without any ambiguity. Concise and explanatory comments contribute to enhanced code readability and easier maintenance. Developers, including yourself, can better understand the context and logic behind code segments, reducing the likelihood of misinterpretation and errors.
deanwhitehouseHO marked this conversation as resolved.
Show resolved Hide resolved

#### Positive Example
```
"""
Calculates the factorial of a given integer.

This function takes an integer as input and returns its factorial. The factorial of a non-negative integer n, denoted as n!, is the product of all positive integers less than or equal to n.
deanwhitehouseHO marked this conversation as resolved.
Show resolved Hide resolved

:param n: The non-negative integer for which to calculate the factorial.
:return: The factorial of the input integer.
"""
def calculate_factorial(n):
if n < 0:
return "Input must be a non-negative integer."
elif n == 0:
return 1
else:
factorial = 1
for i in range(1, n + 1):
factorial *= i
return factorial
```

#### Negative Example
```
"""
This function computes the factorial of an integer, n.

This function takes an integer, n, as input and calculates its factorial. The factorial is calculated for any non-negative integer, denoted as n!, which is found by multiplying all the positive integers less than or equal to n. If the input, n, is negative, an error message is returned. Otherwise, the factorial is calculated using a loop.

:param n: An input integer, which should be a non-negative integer.
:return: The output of the function is the factorial of the provided integer.
"""
def calculate_factorial(n):
if n < 0:
return "An error has occurred. The input should be a non-negative integer."
elif n == 0:
return 1
else:
factorial = 1
for i in range(1, n + 1):
factorial *= i
return factorial
```

### Comments MUST add value to the understanding and readability of the code.

Comments that provide value are essential for code documentation. Comments should not be added thoughtlessly but should genuinely enhance the codebase.

Each comment should contribute to the reader's understanding of the code by providing insights or explanations that might not be immediately evident from the code itself. Value-added comments act as a bridge between the code's functionality and the developer's comprehension, improving accessibility and efficiency for both current and future team members.

deanwhitehouseHO marked this conversation as resolved.
Show resolved Hide resolved
#### Positive Example
```
"""
This function validates user passwords.

This function takes a password as input and checks if it meets security requirements, including minimum length, the presence of both uppercase and lowercase letters, and the use of special characters.

:param password: The user's input password.
:return: True if the password meets security requirements, False otherwise.
"""
def validate_password(password):
# Validation logic here
deanwhitehouseHO marked this conversation as resolved.
Show resolved Hide resolved
```

#### Negative Example
```
"""
This function validates user passwords.

:param password: The user's input password.
:return: True if the password is valid, False if it's not.
"""
def validate_password(password):
# Check if the password is valid
deanwhitehouseHO marked this conversation as resolved.
Show resolved Hide resolved
```

### Comments MUST adhere to a documented standard, such as Docblock, and include all necessary information.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't agree with this.

Sometimes these standards provide some value, but I don't think that all comments should have to comply. - Some of the most useful comments are one-liners that explain something that isn't immediately obvious from reading the code.

In fact I'd suggest that it is these sorts of standards that cause some developers to write the sort of useless comments that are complained about in the section above.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@daniel-ac-martin It is important that a comment should adhere to a standard, if that standard allows one-liners that is fine, but there should be a standard agreed within the code base to ensure that all developers can interpret and write comments consistently

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If such a standard allows free-form comments like that, does it still have value? What would we be achieving by mandating this?

Does this fall into coding standards more generally? i.e. Use a linter / code formatter.


Adhering to a documented standard in your comments can assist in achieving consistency and comprehensiveness. Following established standards, like Docblock, helps harmonize the commenting style across the codebase, simplifying navigation and comprehension for all team members.
deanwhitehouseHO marked this conversation as resolved.
Show resolved Hide resolved

Furthermore, comments should encompass all pertinent information, including parameter descriptions, return values, and function or class descriptions. This practice ensures that any developer reviewing the code possesses all the necessary details to use, modify, or maintain it effectively, reducing the necessity for time-consuming back-and-forths or investigations.
deanwhitehouseHO marked this conversation as resolved.
Show resolved Hide resolved

#### Docblock Information
deanwhitehouseHO marked this conversation as resolved.
Show resolved Hide resolved
- [Docblock - Wikipedia](https://en.wikipedia.org/wiki/Docblock)

##### Language Specific Links (for reference only)
- [JavaDoc](https://www.oracle.com/uk/technical-resources/articles/java/javadoc-tool.html)
- [JSDoc](https://developer.adobe.com/commerce/php/coding-standards/js-docblock/)
- [TSDoc](https://tsdoc.org/)
- [Python Doc String](https://pandas.pydata.org/docs/development/contributing_docstring.html)

#### Positive Example
```
"""
Calculates the factorial of a given integer.

This function takes an integer as input and returns its factorial. The factorial of a non-negative integer n, denoted as n!, is the product of all positive integers less than or equal to n.
Copy link
Contributor

@daniel-ac-martin daniel-ac-martin Nov 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's an irony here that this definition isn't quite right. i.e. 0! = 1 and not 0.

It's difficult when you need to come up with these synthetic examples, of course, and I'm not saying it's a problem.

But I do think we should be aware of this irony, as one argument against comments like these is that one should just read the actual code.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@daniel-ac-martin I guess this plays into the idea that developers shouldn't have to read the code necessarily, especially with IDE tools that pull in comments from methods you shouldn't then need to go into the code to understand it

But yes, creating examples is more of a headache than I expected so I think there's an element of accepting they'll never be perfect. Maybe it's more hassle than it's worth and we should remove them, if they're adding more confusion than clarity?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think I agree @deanwhitehouseHO, maybe the examples are not adding a lot, especially if they cause more confusion than working to help understand the point

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's about doc-block style comments, then maybe we should just have an example with fewer caveats? ;-)

Or perhaps the comment should include a link to wikipedia with the full definition? (I'm not sure how we feel about links in comments? - I find they can be useful at times.)


:param n: The non-negative integer for which to calculate the factorial.
:return: The factorial of the input integer.
Comment on lines +183 to +184
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd argue that the most important information here is the types of the input and output. (When that is not defined in the language itself.)

I also think this example is missing something in that it doesn't tell us how error conditions will be handled. (Or is it just undefined behaviour? But then should this be called out in the comment?) e.g. What happens with calculate_factorial(-1)?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@daniel-ac-martin we've tried to avoid adding in too much information in the examples so as not to dictate the exact approach or syntax of comments - it's definitely a good shout, but i'm not sure it adds any further value other than demonstrating what you can use comments for further

"""
def calculate_factorial(n):
# Factorial calculation logic here
deanwhitehouseHO marked this conversation as resolved.
Show resolved Hide resolved
```

#### Negative Example
```
"""
Calculate factorial, takes a param called `n` which is a number to compute the returned factorial.
"""
def calculate_factorial(n):
# Logic to compute the factorial
deanwhitehouseHO marked this conversation as resolved.
Show resolved Hide resolved
```

### Comments MUST use neutral, unopinionated language and remain applicable and relevant after a code refactor.
deanwhitehouseHO marked this conversation as resolved.
Show resolved Hide resolved

The use of neutral language and ensuring post-refactor relevance in comments is essential for long-term code sustainability. Comments should avoid personal opinions, subjectivity, or emotional language. They should maintain a neutral and factual tone to enable consistent communication.

Furthermore, comments should remain pertinent even after code refactors or modifications. This practice ensures that the information conveyed in the comments stays accurate and valuable as the codebase undergoes changes, thereby reducing the risk of confusion and errors during future modifications.

#### Positive Example
```
"""
Calculates the total price of items in the shopping cart.

This function takes a shopping cart as input and calculates the total price. It iterates through the cart items and sums their prices. The code is designed to work efficiently with various cart structures.

:param cart: The shopping cart as a list of items.
:return: The total price as a float.
"""
def calculate_total_price(cart):
total_price = 0.0
for item in cart:
total_price += item.price
return total_price
```

#### Negative Example
```
"""
This function calculates the total price in a smart way.

This function takes a list of items as input and computes the total price in a highly efficient manner. It's the best way to do this, and it's really smart. You'd be crazy to change it.

:param items: The list of items.
:return: The total price, and trust me, it's the best way to do it.
"""
deanwhitehouseHO marked this conversation as resolved.
Show resolved Hide resolved
def smart_price_calculation(items):
total = 0.0
for item in items:
total += item.price
return total
```

### Comments MUST NOT be present in production deployments.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this point should be included.

There's probably something around saying that debugging information should be removed for production builds, but that's not really about comments.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@daniel-ac-martin this is more important for code that a user can see directly, especially JavaScript

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@deanwhitehouseHO: Ah okay, I think that's a more general point about knowing where you code is running and keeping private information private. I don't think it's specific to comments.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@daniel-ac-martin I agree it's more specific to systems that are out of the developers domain per se (i.e. in a browser), however I feel it is specific to comments as JavaScript primarily will run on a users device with the code served directly to them. Comments in the code, in this case, should be removed before we serve it to the user for a number of reasons - keeping information private is a bit broader, although comments can be encompassed under this, but it touches on a lot more than comments.

It then comes down to, as standards, do we (to an extent) duplicate content that overlaps (i.e. security and comments), or do we have it one place - if one place, how do we decide what goes where. If this fell into a security standard, would a user reading about comments know what to exclude in their comments without reading the standard (and vice versa I suppose).

One way around that is cross linking of course, if we have a standard that covers this then would you mind sharing the link as we may just want to cross link rather than repeat ourselves here


Comments in production deployments present potential security and performance risks. They can unintentionally expose sensitive information, such as API keys or debugging details, to unauthorized individuals. Additionally, comments may have a marginal impact on the code's size, which can affect performance in production environments.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you need to be more clear about what you mean here; I somewhat doubt the premise.

Secrets (e.g. API keys) shouldn't be baked into code full-stop. (And we should employ secret scanning tools to help prevent that.) But that is a separate issue from comments.

You also mention unauthorised individuals but it's not obvious how they would gain access to a 'production deployment' in the first place. - Don't we have bigger problems if we get to that point?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@daniel-ac-martin API keys, of sorts, are often included in web-apps as a way of connecting to backend services so these are baked into the code as is the nature of the technology. This is about ensuring that we aren't giving help to anyone who wishes to understand and attempt to exploit the code in anyway - comments themselves aren't the risk, but the information they may give to potential exploiters are.

With regards to how people may access a system, that is of course a bigger problem, but that doesn't mean we let the guard down elsewhere in my opinion

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to be careful to the extent that we endorse 'security through obscurity' as we shouldn't rely on it as our main defence.

Copy link
Contributor

@robertdeniszczyc2 robertdeniszczyc2 Nov 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is somewhat covered by the Work in the open and Threat modelling principles and patterns.

We actually make reference to reducing 'security by obscurity' in the Work in the open principle, and recommend following central government guidance on open and closed source code.


It is important to ensure that no comments are included in production code to mitigate these issues. The removal of comments from production code enhances security and optimizes code performance, ultimately leading to an improved user experience.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing comments won't alter performance in any meaningful way. (Unless there is something very wrong with the compiler/interpreter.)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@daniel-ac-martin of course, we only mention it in passing as it does alter performance very slightly - do you see any harm in stating this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How slightly?

I do see some harm, as the more content we put out the less likely anyone is to read it. So we need to focus on what matters.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depends on a lot of factors, where we have complex systems in JS and CSS (sorry, this is more my area so i find it easier to highlight) we can have hundreds of lines of comments in a file - these bytes all add up when being served over the network. When we extrapolate that to potentially millions of requests, then it can become fairly significant when it comes to sustainability

I totally get that, we've worked hard to reduce this as much as possible so really appreciate the feedback too!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah definitely this this is more of a HS/CSS problem rather than a Java one

Copy link
Contributor

@daniel-ac-martin daniel-ac-martin Nov 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is all about client-side JavaScript, I wonder if this is really a more general point about minimising your javascript. i.e. We should cover it elsewhere.


deanwhitehouseHO marked this conversation as resolved.
Show resolved Hide resolved
#### Tools (for reference only)
- **React/JavaScript/TypeScript/Node:**
- [Terser](https://terser.org/)
- [Babel](https://babeljs.io/)

- **HTML/CSS:**
- [HTML Minifier](https://www.npmjs.com/package/html-minifier)
- [CSS Minifier](https://www.npmjs.com/package/css-minify)

#### Example
```
# This is a development comment that provides insights for developers.
def calculate_price(item):
# Calculate the price based on item properties
return item.base_price * item.discount

# In production code, comments are removed to enhance performance and security.
def calculate_price(item):
return item.base_price * item.discount
```
deanwhitehouseHO marked this conversation as resolved.
Show resolved Hide resolved

### Comments MUST NOT be duplicated or be used as a substitute for deleting code.
deanwhitehouseHO marked this conversation as resolved.
Show resolved Hide resolved

Duplication of comments and using comments as a replacement for code deletion can impact code cleanliness. Developers are encouraged to steer clear of duplicating comments across various code sections, as it may result in inconsistencies when modifications are required. Rather than adding a comment to elucidate outdated or unnecessary code, it is advisable to remove the unused code entirely.
deanwhitehouseHO marked this conversation as resolved.
Show resolved Hide resolved

Replacing code deletion with comments can introduce confusion and clutter within the codebase. Ensuring that comments are employed for authentic explanations and not as a workaround for code removal contributes to a cleaner and more maintainable codebase.

#### Positive Example
```
"""
This function calculates the area of a rectangle.

:param length: The length of the rectangle.
:param width: The width of the rectangle.
:return: The area of the rectangle.
"""
def calculate_rectangle_area(length, width):
return length * width
```

#### Negative Example
```
"""
This function calculates the area of a rectangle.

:param length: The length of the rectangle.
:param width: The width of the rectangle.
:return: The area of the rectangle.
"""
def calculate_rectangle_area(length, width):
# Calculate the area of the rectangle
return length * width
# return width * length # Not used
```

### Comments MUST NOT contain sensitive data, such as API keys, tokens, etc.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a more general point around managing secrets. I don't think it should be covered here except perhaps to link off to another standard / pattern / guidance.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@daniel-ac-martin can you please provide the link to the standard that we can link to please?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not certain if we have one published publicly at this time. We've got stuff written down elsewhere that we could re-use.

If we don't have it, I'd suggest removing it here and raising an issue to add a link when we do.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a Standard around managing secrets: https://engineering.homeoffice.gov.uk/standards/managing-secrets/


Avoiding sensitive data in comments is a fundamental security practice. Storing confidential information, such as API keys or tokens, within comments can pose substantial security risks. Comments are typically part of the source code repository and may be accessible to unauthorized individuals.

To safeguard sensitive data and maintain the security of your application, it's essential to refrain from including such information within comments. Instead, it's advisable to utilize secure and designated storage mechanisms for sensitive data, guaranteeing the functionality and security of your code.

#### Good Example
```
"""
Connect to the database using secure credentials.

This function establishes a database connection using secure credentials stored in a separate, protected configuration file. Sensitive data, such as database usernames and passwords, are not exposed in comments or within the source code.

:return: A database connection object.
"""
def connect_to_database():
# Database connection logic here
```

#### Bad Example
```
"""
Connect to the database using username 'my_user' and password 'my_password'.

This function establishes a database connection using hard-coded sensitive data within the comment itself. Storing sensitive data in this manner is a security risk and should be avoided.

:return: A database connection object.
"""
def connect_to_database():
# Database connection logic here
```