README¶
Premier point de contact
Le README fait partie de la documentation car il fournit des informations essentielles sur le projet, telles que son installation, son utilisation et sa configuration. Il sert de guide d’introduction pour les utilisateurs et les développeurs, facilitant la compréhension et l’accès au projet.
Examples de README
Licence¶
Example de la Licence MIT, une licence open-source très permissive :
Copyright <YEAR> <COPYRIGHT HOLDER>
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the “Software”), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.Documentation du code¶
Example de documentation en Doxygen (qui est très similaire à Javadoc) :
#ifndef TEMPERATURE_H
#define TEMPERATURE_H
/**
* Represents a temperature in Celsius and Fahrenheit.
*
* This class allows you to store a temperature in Celsius, convert it to
* Fahrenheit, and update it from either Celsius or Fahrenheit values.
*
* @par Example
* @code{.cpp}
* Temperature currentTemperature(25.0);
*
* currentTemperature.displayTemperature();
*
* // Output:
* // Temperature: 25°C / 77°F
* @endcode
*/
class Temperature {
private:
double mCelsius;
void validateCelsiusTemperature(double celsius) const;
void validateFahrenheitTemperature(double fahrenheit) const;
public:
/**
* Constructs a new Temperature object.
*
* Initializes the temperature in Celsius.
*
* Absolute zero is −273.15°C.
*
* @param celsius The initial temperature in Celsius.
*
* @throws std::invalid_argument if celsius is below absolute zero.
*
* @par Example
* @code{.cpp}
* Temperature currentTemperature(25.0);
*
* // Temperature stored: 25°C
* @endcode
*
* @par Invalid Example
* @code{.cpp}
* Temperature invalidTemperature(-300.0);
*
* // Throws std::invalid_argument
* @endcode
*/
Temperature(double celsius);
/**
* Gets the current temperature in Celsius.
*
* @return The temperature in Celsius.
*
* @par Example
* @code{.cpp}
* Temperature currentTemperature(25.0);
*
* double temperature = currentTemperature.getCelsius();
*
* // temperature == 25.0
* @endcode
*/
double getCelsius() const;
/**
* Sets the temperature in Celsius.
*
* This method directly updates the Celsius value.
*
* Absolute zero is −273.15°C.
*
* @param celsius The new temperature in Celsius.
*
* @throws std::invalid_argument if celsius is below absolute zero.
*
* @par Example
* @code{.cpp}
* Temperature currentTemperature(25.0);
*
* currentTemperature.setCelsius(10.0);
*
* // Temperature stored: 10°C
* @endcode
*
* @par Invalid Example
* @code{.cpp}
* Temperature currentTemperature(25.0);
*
* currentTemperature.setCelsius(-300.0);
*
* // Throws std::invalid_argument
* @endcode
*/
void setCelsius(double celsius);
/**
* Gets the current temperature in Fahrenheit.
*
* @return The temperature in Fahrenheit.
*
* @par Example
* @code{.cpp}
* Temperature currentTemperature(25.0);
*
* double temperature = currentTemperature.getFahrenheit();
*
* // temperature == 77.0
* @endcode
*/
double getFahrenheit() const;
/**
* Sets the temperature using a value in Fahrenheit.
*
* Converts the Fahrenheit value to Celsius before storing it.
*
* Absolute zero is −459.67°F.
*
* @param fahrenheit The temperature in Fahrenheit.
*
* @throws std::invalid_argument if fahrenheit is below absolute zero.
*
* @par Example
* @code{.cpp}
* Temperature currentTemperature(25.0);
*
* currentTemperature.setFahrenheit(100.0);
*
* // Temperature stored: approximately 37.8°C
* @endcode
*
* @par Invalid Example
* @code{.cpp}
* Temperature currentTemperature(25.0);
*
* currentTemperature.setFahrenheit(-500.0);
*
* // Throws std::invalid_argument
* @endcode
*/
void setFahrenheit(double fahrenheit);
/**
* Displays the temperature in both Celsius and Fahrenheit.
*
* This method prints the current temperature in both units.
*
* @par Example
* @code{.cpp}
* Temperature currentTemperature(25.0);
*
* currentTemperature.displayTemperature();
*
* // Output:
* // Temperature: 25°C / 77°F
* @endcode
*/
void displayTemperature() const;
};
#endifLa syntaxe Doxygen/Javadoc :
/**
* One sentence describing the class/method.
*
* More detailed descriptions here with one or multiple sentences.
*
* @param parameterName Parameter description.
* @return Output description if it exists.
* @throws ExceptionThrown Exception description if it exists
* //@exception for older version of Java/Javadoc (< 17).
*/Le même exemple en Javadoc
/**
* Represents a temperature in Celsius and Fahrenheit.
*
* This class allows you to store a temperature in Celsius, convert it to
* Fahrenheit, and update it from either Celsius or Fahrenheit values.
*
* Example:
* <pre>{@code
* Temperature currentTemperature = new Temperature(25.0);
*
* currentTemperature.displayTemperature();
*
* // Output:
* // Temperature: 25.0°C / 77.0°F
* }</pre>
*/
public class Temperature {
private double celsius;
private void validateCelsiusTemperature(double celsius) {
// ...
}
private void validateFahrenheitTemperature(double fahrenheit) {
// ...
}
/**
* Constructs a new Temperature object.
*
* Initializes the temperature in Celsius.
*
* Absolute zero is -273.15°C.
*
* @param celsius the initial temperature in Celsius
*
* @throws IllegalArgumentException if celsius is below absolute zero
*
* Example:
* <pre>{@code
* Temperature currentTemperature = new Temperature(25.0);
*
* // Temperature stored: 25.0°C
* }</pre>
*
* Invalid example:
* <pre>{@code
* Temperature invalidTemperature = new Temperature(-300.0);
*
* // Throws IllegalArgumentException
* }</pre>
*/
public Temperature(double celsius) {
// ...
}
/**
* Gets the current temperature in Celsius.
*
* @return the temperature in Celsius
*
* Example:
* <pre>{@code
* Temperature currentTemperature = new Temperature(25.0);
*
* double temperature = currentTemperature.getCelsius();
*
* // temperature == 25.0
* }</pre>
*/
public double getCelsius() {
// ...
}
/**
* Sets the temperature in Celsius.
*
* This method directly updates the Celsius value.
*
* Absolute zero is -273.15°C.
*
* @param celsius the new temperature in Celsius
*
* @throws IllegalArgumentException if celsius is below absolute zero
*
* Example:
* <pre>{@code
* Temperature currentTemperature = new Temperature(25.0);
*
* currentTemperature.setCelsius(10.0);
*
* // Temperature stored: 10.0°C
* }</pre>
*
* Invalid example:
* <pre>{@code
* Temperature currentTemperature = new Temperature(25.0);
*
* currentTemperature.setCelsius(-300.0);
*
* // Throws IllegalArgumentException
* }</pre>
*/
public void setCelsius(double celsius) {
// ...
}
/**
* Gets the current temperature in Fahrenheit.
*
* @return the temperature in Fahrenheit
*
* Example:
* <pre>{@code
* Temperature currentTemperature = new Temperature(25.0);
*
* double temperature = currentTemperature.getFahrenheit();
*
* // temperature == 77.0
* }</pre>
*/
public double getFahrenheit() {
// ...
}
/**
* Sets the temperature using a value in Fahrenheit.
*
* Converts the Fahrenheit value to Celsius before storing it.
*
* Absolute zero is -459.67°F.
*
* @param fahrenheit the temperature in Fahrenheit
*
* @throws IllegalArgumentException if fahrenheit is below absolute zero
*
* Example:
* <pre>{@code
* Temperature currentTemperature = new Temperature(25.0);
*
* currentTemperature.setFahrenheit(100.0);
*
* // Temperature stored: approximately 37.8°C
* }</pre>
*
* Invalid example:
* <pre>{@code
* Temperature currentTemperature = new Temperature(25.0);
*
* currentTemperature.setFahrenheit(-500.0);
*
* // Throws IllegalArgumentException
* }</pre>
*/
public void setFahrenheit(double fahrenheit) {
// ...
}
/**
* Displays the temperature in both Celsius and Fahrenheit.
*
* This method prints the current temperature in both units.
*
* Example:
* <pre>{@code
* Temperature currentTemperature = new Temperature(25.0);
*
* currentTemperature.displayTemperature();
*
* // Output:
* // Temperature: 25.0°C / 77.0°F
* }</pre>
*/
public void displayTemperature() {
// ...
}
}Commenter¶
Choix d’implémentation¶
// Insertion sort is chosen for simplicity since the data set is smallNote
Un commentaire expliquant un choix d’implémentation, lorsqu’il existe plusieurs options possibles, peut être utile. Même si un autre développeur n’est pas d’accord avec le choix fait, il pourra comprendre le raisonnement sous-jacent et l’améliorer si nécessaire.
Clarifications¶
double calculateLoanPayment(double principal, double annualRate, int months) {
// Formula: P = (r*PV) / (1 - (1 + r)^(-n))
// r = monthly interest rate, PV = present value (loan amount), n = number of payments
double monthlyRate = annualRate / 12;
return (monthlyRate * principal) / (1 - pow(1 + monthlyRate, -months));
}Formules ou magic numbers
Le commentaire explique les composants de la formule et son application. Sans ce commentaire, une personne non familière avec la formule pourrait avoir du mal à saisir qu’il s’agit d’une formule mathématique, et non un choix d’implémentation technique.
// format matched hh:mm:ss MM dd, yyyy
regex timeMatcher("\\d*:\\d*:\\d* \\w* \\d*, \\d*");Expressions régulières
Les expressions régulières, bien que souvent complexes, sont indispensables. Il est donc utile d’expliquer celle utilisée dans ce cas.
De façon générale, il faut clarifier tout endroit où l’intention et/ou le contexte du code n’est pas clair.
Warning¶
// Dear maintainer:
//
// Once you are done trying to 'optimize' this routine,
// and have realized what a terrible mistake that was,
// please increment the following counter as a warning
// to the next guy:
//
// total_hours_wasted_here = 42Plus sérieusement,
Parfois, il est utile de prévenir un autre développeur d’une erreur souvent rencontrée (quand on essaye d’améliorer un code par exemple) ou souligner l’importance d’une procédure qui pourrait sembler inconséquente.
// the trim is real important. It removes the starting
// spaces that could cause the item to be recognized
// as another list.
string listItemContent = trim(match.str(3));TODO¶
// TODO : sorting procedure to be implementedMauvais commentaires¶
Beaucoup de commentaires sont une excuse pour du code de mauvaise qualité. Voici les erreurs à éviter.
Commentaires cryptiques¶
La racine carrée inverse rapide.
float Q_rsqrt( float number )
{
long i;
float x2, y;
const float threehalfs = 1.5F;
x2 = number * 0.5F;
y = number;
i = * ( long * ) &y; // evil floating point bit level hacking
i = 0x5f3759df - ( i >> 1 ); // what the fuck?
y = * ( float * ) &i;
y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed
return y;
}Une version améliorée avec la documentation dans le header :
/**
* Computes an approximation of 1 / sqrt(number) using the Fast Inverse Square Root method.
*
* This function is based on the original Quake III Arena algorithm.
* It uses bit-level manipulation to obtain a rough approximation, followed by
* one iteration of Newton-Raphson refinement for improved accuracy.
*
* Explanation:
* - https://en.wikipedia.org/wiki/Fast_inverse_square_root
*
* @param number The input floating-point number.
* @return An approximation of 1 / sqrt(number).
*/et le code suivant :
float fastInverseSquareRoot(float number) {
static const uint32_t approximationConstant = 0x5F3759DF;
static const float newtonRaphsonFactor = 1.5F;
// Interpret the float as an integer for bitwise operations
union {
float approximation; // Holds the estimated 1/sqrt(number)
uint32_t bitwiseRepresentation; // Stores the bitwise equivalent of the float
} inverseSqrtData = { .approximation = number };
// Initial approximation using bitwise trick
inverseSqrtData.bitwiseRepresentation = approximationConstant - (inverseSqrtData.bitwiseRepresentation >> 1);
// One iteration of Newton-Raphson refinement
// A second iteration can improve precision at the cost of time
inverseSqrtData.approximation *= newtonRaphsonFactor - (0.5F * number * inverseSqrtData.approximation * inverseSqrtData.approximation);
return inverseSqrtData.approximation;
}Documenter les headers, commenter les codes source en C++
En C++, nous documentons uniquement les headers qui définissent l’interface publique du projet, tandis que les commentaires dans le code source sont réservés aux développeurs.
Dans des langages comme Java, qui ne font pas cette distinction, vous trouverez à la fois de la documentation et des commentaires dans le même fichier.
Commentaires avec trop d’informations¶
/*
RFC 2045 - Multipurpose Internet Mail Extensions (MIME)
Part One: Format of Internet Message Bodies
section 6.8. Base64 Content-Transfer-Encoding
The encoding process represents 24-bit groups of input bits as output
strings of 4 encoded characters. Proceeding from left to right, a
24-bit input group is formed by concatenating 3 8-bit input groups.
These 24 bits are then treated as 4 concatenated 6-bit groups, each
of which is translated into a single digit in the base64 alphabet.
When encoding a bit stream via the base64 encoding, the bit stream
must be presumed to be ordered with the most-significant-bit first.
That is, the first bit in the stream will be the high-order bit in
the first 8-bit byte, and the eighth bit will be the low-order bit in
the first 8-bit byte, and so on.
*/Commentaires redondants¶

Credits: Programmers Humor.
Commenter au lieu de coder proprement¶
bool isPasswordValid(const string& password) {
// Pattern matches that:
// - Must be between 8 and 20 digits
// - Must contain at least one uppercase letter and one lowercase letter
// - Must contain one number
// - Must not include whitespace
// - Must contain one of the following special characters: ! # $ % ^ & *
regex pattern("^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[!#$%^&*])[\\S]{8,20}$");
return regex_match(password, pattern);
}Un bon commentaire ?
Le commentaire ci-dessus peut sembler utile puisqu’il explique l’expression régulière difficile à lire utilisée pour valider un mot de passe.
Cependant, ces commentaires ne font que masquer un mauvais code. En suivant les principes du code propre, la fonction isPasswordValid devrait être refactorisée comme suit.
bool lengthIsInRangeInclusive(const string& str) {
return str.length() >= 8 && str.length() <= 20;
}
bool containsUppercaseLetter(const string& str) {
return regex_match(str, regex("(?=.*[A-Z]).*"));
}
bool containsLowercaseLetter(const string& str) {
return regex_match(str, regex("(?=.*[a-z]).*"));
}
bool containsNumericDigit(const string& str) {
return regex_match(str, regex("(?=.*[0-9]).*"));
}
bool doesNotContainWhitespace(const string& str) {
return !regex_match(str, regex("(?=.*[\\s]).*"));
}
bool containsSpecialCharacter(const string& str) {
return regex_match(str, regex("(?=.*[!#$%^&*\\.]).*"));
}
bool isPasswordValid(const string& password) {
return lengthIsInRangeInclusive(password) &&
containsUppercaseLetter(password) &&
containsLowercaseLetter(password) &&
containsNumericDigit(password) &&
doesNotContainWhitespace(password) &&
containsSpecialCharacter(password);
}Laisser du code commenté¶
InputStreamResponse response = new InputStreamResponse();
response.setBody(formatter.getResultStream(), formatter.getByteCount());
//InputStream resultsStream = formatter.getResultStream();
//StreamReader reader = new StreamReader(resultsStream);
//response.setContent(reader.read(formatter.getByteCount()));Danger
Il arrive souvent de commenter des lignes de code pendant le développement. Cependant, ces commentaires ne doivent pas rester sans explication. Si un autre programmeur lit ce code sans comprendre l’objectif des lignes commentées, il n’osera probablement pas les supprimer. Ainsi, elles risquent de demeurer dans le code, sans justification, et d’induire les futurs développeurs en erreur.
Avec les outils de versionnage comme Git, il est presque impossible de perdre du code (si vous effectuez des commits réguliers !). N’ayez donc pas peur de supprimer du code et de le retrouver plus tard dans l’historique des commits.
Commenter au lieu de versionner¶
/*
11-Oct-2001 : Re-organised the class and moved it to new package com.jrefinery.date (DG);
05-Nov-2001 : Added a getDescription() method, and eliminated NotableDate class (DG);
05-Dec-2001 : Fixed bug in SpreadsheetDate class (DG);
*/Danger
Certains pourraient penser qu’il est utile de laisser des notes sur l’évolution du code dans les commentaires pour les autres développeurs.
En effet, cela pourrait être une bonne pratique si Git n’existait pas. Avec le versionnage, un simple git log suffit (à condition bien sûr que les messages de commits soient clairs) !