Developer Area/How to Review Code: Difference between revisions
From Mahara Wiki
< Developer Area
(link to commit policy) |
(→Don'ts) |
||
(29 intermediate revisions by 5 users not shown) | |||
Line 1: | Line 1: | ||
Here are the things to look for when reviewing code on the [https://reviews.mahara.org Mahara Gerrit]. | Here are the things to look for when reviewing code on the [https://reviews.mahara.org Mahara Gerrit]. These are not absolute rules, they are guidelines. | ||
Also see our [[Developer_Area/Code_Review|commit / review policy]]. | Also see our [[Developer_Area/Code_Review|commit / review policy]]. | ||
Line 5: | Line 5: | ||
= Basic guidelines = | = Basic guidelines = | ||
# If you're the first reviewer, you can +1 or +2 a commit. | # If you're the first reviewer, you can +1 or +2 a commit. | ||
# If you're reviewing something that's already +1, you cannot just give it another +1. You must take the other review into consideration and give it a +2 or you directly invite another person to review and give a +1. | # If you're reviewing something that's already +1, you cannot just give it another +1. You must take the other review into consideration and give it a +2 or you directly invite another person to review and give a +1. | ||
= Verification = | |||
The rest of the guidelines apply to the "code review" part of Gerrit. These ones are for the testing or "Verified" part: | |||
* If you submit a change, you '''can''' mark it as verified unless you specifically want others to test it. | |||
* Only mark as verified a change that you have tested yourself by checking out the right review branch (use the command line given by Gerrit). | |||
* When appropriate, briefly describe what tests you did in your review. For example "tested fresh install on Postgres and MySQL, works fine" | |||
= Backward compatible code = | |||
Backward compatibility is usually encouraged in Mahara. This mostly affects the parts of Mahara where different pieces of code interact with each other (the "[https://en.wikipedia.org/wiki/Interface_(computing)#Software_interfaces_in_practice interfaces]"). | |||
* function definitions (names, parameters, return values) | |||
* class definitions | |||
* expected Dwoo template variables (for templates that are called by many scripts, such as header.tpl) | |||
Changing these sorts of things, can cause bugs in code that still expects the old interface to be there. This may be code from third-party plugins, or code that was missed while refactoring (perhaps because it's in a Dwoo template, or the function name is pieced together from multiple strings). | |||
== Don'ts == | |||
Avoid doing any of these things, which are likely to cause non-upgraded code to crash with ugly errors: | |||
* Change the names of functions, classes, constants, Dwoo template variables. | |||
* Change the filenames of PHP scripts that are not "INTERNAL". | |||
** e.g. moving <tt>htdocs/view/artefact.php</tt> to <tt>htdocs/artefact/artefact.php</tt> | |||
* Change the order of existing function parameters. | |||
* Remove existing function parameters. | |||
* Add new parameters in the middle of the list. | |||
* Add a new parameter with no default value. | |||
* Change the data type of existing parameters. | |||
* Add new methods to a PHP [http://php.net/manual/en/language.oop5.interfaces.php interface]. | |||
* Add new abstract methods to an [http://php.net/manual/en/language.oop5.abstract.php abstract class]. | |||
'''Workarounds''': In some cases, you can provide a workaround so that non-updated code will continue to function. | |||
* If the data type of a parameter has changed, you can add code that detects the data type of the value passed for that parameter, and convert it to the correct data type. | |||
* If the name of a function must change, you can provide a "passthrough" function under the old name, that just logs a debug message and then invokes the new function. | |||
* etc... | |||
== Do's == | |||
It's okay to break backwards compatibility in these cases: | |||
1. '''Still in development''': A new feature/function/class still in code review can of course be changed as much as you want. Or, if it has been pushed to "master", but hasn't yet been included in a release candidate or major release. | |||
2. '''Major version upgrades''': If you must break backwards compatibility on an existing stable feature, then do it on a major version upgrade. And add a note about it in the '''release notes'''. | |||
3. '''Urgent security issues''': A high-priority security issue may be worth breaking backwards compatibility outside of the normal release schedule. | |||
4. '''Private / "internal" code''': If a piece of code is clearly marked as being an internal implementation detail, then it's less risky to break BC because third-party code should not be using it. | |||
= Database = | |||
* Make sure there are no unnecessary DB queries being added | |||
* Watch out for queries inside loops (do it outside the loop all at once in a single query) | |||
* Table names in queries should be surrounded by {braces} so that prefixes will be applied if needed. | |||
* The name length of a table, column, index, constraint, view, function or stored program including the $cfg->dbprefix must be less than 64 characters to make it compatible to MySQL 5.x | |||
* SQL injections | |||
** use placeholders in queries, no exceptions | |||
** ideally, do the validation of the parameters close to where they're used so that it's obvious that it's validated | |||
* When the database schema is changed, the '''same changes''' should be made in the install.xml and in the upgrade.php | |||
* Version bumps on the stable branch '''should not use the current date''', they should just add 1 to the version number. | |||
= Misc stuff = | = Misc stuff = | ||
Line 13: | Line 75: | ||
A few things which don't fit in the other categories but are important to keep in mind: | A few things which don't fit in the other categories but are important to keep in mind: | ||
* Make sure loop variables are properly initialized at the start and/or each iteration of the loop. | |||
** In general be careful of re-using variables. Make sure to wipe the variable completely so that, e.g. data from one database record doesn't get inadvertently written onto another one. | |||
* Watch out for <tt>create_function()</tt> inside a loop. These functions go in the global namespace and are never garbage collected, so they can easily cause out-of-memory errors. | |||
* [[Developer_Area/Coding_guidelines|Coding guidelines]] should be followed unless there's a good reason not to. | * [[Developer_Area/Coding_guidelines|Coding guidelines]] should be followed unless there's a good reason not to. | ||
* Curl: don't use directly, use <tt>htdocs/lib/web.php:mahara_http_request()</tt> instead | * Curl: don't use directly, use <tt>htdocs/lib/web.php:mahara_http_request()</tt> instead | ||
Line 18: | Line 83: | ||
* No <tt>DIRECTORY_SEPARATOR</tt>: always use the UNIX path separator when opening files (it works on Windows NT too) | * No <tt>DIRECTORY_SEPARATOR</tt>: always use the UNIX path separator when opening files (it works on Windows NT too) | ||
* Too much inline HTML? Please move it to a Dwoo template. | * Too much inline HTML? Please move it to a Dwoo template. | ||
* JSON: new code should always use json_reply() rather than json_headers(), etc. | |||
* HTTP headers: when writing code that assumes the presence of HTTP headers, think about '''cron running in CLI mode''' | |||
* Slow operations should ideally be done in cron | |||
= Commit message = | = Commit message = | ||
Line 23: | Line 91: | ||
The commit message is part of what we review. This article about [http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html great git commit messages] is a good start. Other things to watch for: | The commit message is part of what we review. This article about [http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html great git commit messages] is a good start. Other things to watch for: | ||
* | * Unless the commit is trivial (e.g. typo fix, minor coding guideline cleanup), it should have a bug on the tracker and the bug number should be included somewhere (doesn't have to be the summary line) in the commit message like this: "bug #123456" or "Bug #123456" | ||
* If the bug is generic and relates to a blueprint, put in a tag to link it to that instead "Blueprint: blueprint-name" | |||
* Click on the bug/blueprint number. Is it the right bug/blueprint? | |||
* Think of the reviewer: your "commit message + the diff" needs to tell a story: why you changed the code in that way. The reviewer may need to go back to the bug on the tracker to see how to reproduce exactly the original problem, but your solution should be clearly explained in the commit message if it's not obvious from the code. | * Think of the reviewer: your "commit message + the diff" needs to tell a story: why you changed the code in that way. The reviewer may need to go back to the bug on the tracker to see how to reproduce exactly the original problem, but your solution should be clearly explained in the commit message if it's not obvious from the code. | ||
* When you fix a bug something introduced in a previous (recent-ish) commit, mention the commit hash in your commit message. | * When you fix a bug something introduced in a previous (recent-ish) commit, mention the commit hash in your commit message. | ||
Line 33: | Line 103: | ||
* Check for grammar / typos | * Check for grammar / typos | ||
= | = Templates = | ||
* HTML element "style=" attributes in the templates are discouraged because it makes it impossible to theme these elements. Move this css stuff to the raw stylesheet instead. | |||
* Check for cross-site scripting problems: | * Check for cross-site scripting problems: | ||
** watch out for <tt>|safe</tt> in templates | ** watch out for <tt>|safe</tt> in templates, it should only be there when the HTML is outputs is '''not coming from the user''' (or has been cleaned through Purifier) | ||
** templates should be escaped by default | ** templates should be escaped by default | ||
* HTML comments should be avoided. Instead use a Dwoo comment like this: <tt>{* this is a comment that won't be in the final HTML output *}</tt> | |||
= Forms = | |||
* Check for cross-site request forgeries | * Check for cross-site request forgeries | ||
** forms should ideally use pieforms which has a built-in session key check | ** forms should ideally use pieforms which has a built-in session key check | ||
** non-pieforms forms should include the sesskey and check it on submission | |||
** other operations that change the state of the application need to check the session key before doing anything | ** other operations that change the state of the application need to check the session key before doing anything | ||
= Javascript / CSS = | |||
* | |||
* | * Javascript and CSS is [https://developer.mozilla.org/Writing_Forward_Compatible_Websites forward-compatible] | ||
* Javascript has semicolons at the end of each line (for maximum compatibility) | |||
= New | = New external libraries = | ||
* our patches are maintained but not mixed in with upstream code | * our patches are maintained but not mixed in with upstream code | ||
* | * check that the library doesn't add new dependencies | ||
* check if there are new copyright statements (which could lead to GPL-incompatible code being added to our codebase) | * check if there are new copyright statements (which could lead to GPL-incompatible code being added to our codebase) | ||
* make sure a README.Mahara exists for that external component. look at htdocs/lib/htmlpurifier/README.Mahara for a sample one | |||
* do '''not''' change upstream code at all in the commit where you add it to Mahara. if you need to patch it to fix a bug, do that in a separate commit and make a note in its README.Mahara | |||
* we shouldn't fix coding-guidelines related problems in external code. Delete the Jenkins review and link to the relevant console output in your review (e.g. https://jenkins.mahara.org/job/mahara/414/console) | |||
= Useful links = | |||
* [https://wiki.mahara.org/index.php/Developer_Area/Commit_Policy Commit and revision policy] | |||
* [http://www.slideshare.net/rkabalin/how-mahara-code-revision-works How Mahara code revision works] - presentation on Mahara UK 11 technical workshop. |
Latest revision as of 12:57, 7 August 2017
Here are the things to look for when reviewing code on the Mahara Gerrit. These are not absolute rules, they are guidelines.
Also see our commit / review policy.
Basic guidelines
- If you're the first reviewer, you can +1 or +2 a commit.
- If you're reviewing something that's already +1, you cannot just give it another +1. You must take the other review into consideration and give it a +2 or you directly invite another person to review and give a +1.
Verification
The rest of the guidelines apply to the "code review" part of Gerrit. These ones are for the testing or "Verified" part:
- If you submit a change, you can mark it as verified unless you specifically want others to test it.
- Only mark as verified a change that you have tested yourself by checking out the right review branch (use the command line given by Gerrit).
- When appropriate, briefly describe what tests you did in your review. For example "tested fresh install on Postgres and MySQL, works fine"
Backward compatible code
Backward compatibility is usually encouraged in Mahara. This mostly affects the parts of Mahara where different pieces of code interact with each other (the "interfaces").
- function definitions (names, parameters, return values)
- class definitions
- expected Dwoo template variables (for templates that are called by many scripts, such as header.tpl)
Changing these sorts of things, can cause bugs in code that still expects the old interface to be there. This may be code from third-party plugins, or code that was missed while refactoring (perhaps because it's in a Dwoo template, or the function name is pieced together from multiple strings).
Don'ts
Avoid doing any of these things, which are likely to cause non-upgraded code to crash with ugly errors:
- Change the names of functions, classes, constants, Dwoo template variables.
- Change the filenames of PHP scripts that are not "INTERNAL".
- e.g. moving htdocs/view/artefact.php to htdocs/artefact/artefact.php
- Change the order of existing function parameters.
- Remove existing function parameters.
- Add new parameters in the middle of the list.
- Add a new parameter with no default value.
- Change the data type of existing parameters.
- Add new methods to a PHP interface.
- Add new abstract methods to an abstract class.
Workarounds: In some cases, you can provide a workaround so that non-updated code will continue to function.
- If the data type of a parameter has changed, you can add code that detects the data type of the value passed for that parameter, and convert it to the correct data type.
- If the name of a function must change, you can provide a "passthrough" function under the old name, that just logs a debug message and then invokes the new function.
- etc...
Do's
It's okay to break backwards compatibility in these cases:
1. Still in development: A new feature/function/class still in code review can of course be changed as much as you want. Or, if it has been pushed to "master", but hasn't yet been included in a release candidate or major release.
2. Major version upgrades: If you must break backwards compatibility on an existing stable feature, then do it on a major version upgrade. And add a note about it in the release notes.
3. Urgent security issues: A high-priority security issue may be worth breaking backwards compatibility outside of the normal release schedule.
4. Private / "internal" code: If a piece of code is clearly marked as being an internal implementation detail, then it's less risky to break BC because third-party code should not be using it.
Database
- Make sure there are no unnecessary DB queries being added
- Watch out for queries inside loops (do it outside the loop all at once in a single query)
- Table names in queries should be surrounded by {braces} so that prefixes will be applied if needed.
- The name length of a table, column, index, constraint, view, function or stored program including the $cfg->dbprefix must be less than 64 characters to make it compatible to MySQL 5.x
- SQL injections
- use placeholders in queries, no exceptions
- ideally, do the validation of the parameters close to where they're used so that it's obvious that it's validated
- When the database schema is changed, the same changes should be made in the install.xml and in the upgrade.php
- Version bumps on the stable branch should not use the current date, they should just add 1 to the version number.
Misc stuff
A few things which don't fit in the other categories but are important to keep in mind:
- Make sure loop variables are properly initialized at the start and/or each iteration of the loop.
- In general be careful of re-using variables. Make sure to wipe the variable completely so that, e.g. data from one database record doesn't get inadvertently written onto another one.
- Watch out for create_function() inside a loop. These functions go in the global namespace and are never garbage collected, so they can easily cause out-of-memory errors.
- Coding guidelines should be followed unless there's a good reason not to.
- Curl: don't use directly, use htdocs/lib/web.php:mahara_http_request() instead
- No magic numbers: use constants instead of hard-coded numbers as much as possible
- No DIRECTORY_SEPARATOR: always use the UNIX path separator when opening files (it works on Windows NT too)
- Too much inline HTML? Please move it to a Dwoo template.
- JSON: new code should always use json_reply() rather than json_headers(), etc.
- HTTP headers: when writing code that assumes the presence of HTTP headers, think about cron running in CLI mode
- Slow operations should ideally be done in cron
Commit message
The commit message is part of what we review. This article about great git commit messages is a good start. Other things to watch for:
- Unless the commit is trivial (e.g. typo fix, minor coding guideline cleanup), it should have a bug on the tracker and the bug number should be included somewhere (doesn't have to be the summary line) in the commit message like this: "bug #123456" or "Bug #123456"
- If the bug is generic and relates to a blueprint, put in a tag to link it to that instead "Blueprint: blueprint-name"
- Click on the bug/blueprint number. Is it the right bug/blueprint?
- Think of the reviewer: your "commit message + the diff" needs to tell a story: why you changed the code in that way. The reviewer may need to go back to the bug on the tracker to see how to reproduce exactly the original problem, but your solution should be clearly explained in the commit message if it's not obvious from the code.
- When you fix a bug something introduced in a previous (recent-ish) commit, mention the commit hash in your commit message.
- If adding a new feature, briefly describe the feature, don't assume the reviewer will know all about the fancy Web 2.0 service you're adding a plugin for (at the very least in this case, you should include the URL of the service).
Langstrings / help files
- Apart from messages to the logs, everything should be translatable
- Check for grammar / typos
Templates
- HTML element "style=" attributes in the templates are discouraged because it makes it impossible to theme these elements. Move this css stuff to the raw stylesheet instead.
- Check for cross-site scripting problems:
- watch out for |safe in templates, it should only be there when the HTML is outputs is not coming from the user (or has been cleaned through Purifier)
- templates should be escaped by default
- HTML comments should be avoided. Instead use a Dwoo comment like this: {* this is a comment that won't be in the final HTML output *}
Forms
- Check for cross-site request forgeries
- forms should ideally use pieforms which has a built-in session key check
- non-pieforms forms should include the sesskey and check it on submission
- other operations that change the state of the application need to check the session key before doing anything
Javascript / CSS
- Javascript and CSS is forward-compatible
- Javascript has semicolons at the end of each line (for maximum compatibility)
New external libraries
- our patches are maintained but not mixed in with upstream code
- check that the library doesn't add new dependencies
- check if there are new copyright statements (which could lead to GPL-incompatible code being added to our codebase)
- make sure a README.Mahara exists for that external component. look at htdocs/lib/htmlpurifier/README.Mahara for a sample one
- do not change upstream code at all in the commit where you add it to Mahara. if you need to patch it to fix a bug, do that in a separate commit and make a note in its README.Mahara
- we shouldn't fix coding-guidelines related problems in external code. Delete the Jenkins review and link to the relevant console output in your review (e.g. https://jenkins.mahara.org/job/mahara/414/console)
Useful links
- Commit and revision policy
- How Mahara code revision works - presentation on Mahara UK 11 technical workshop.