mirror of
https://git.openafs.org/openafs.git
synced 2025-01-18 06:50:12 +00:00
CODING: add code review guidelines
Attempt to enumerate things that reviewers should look for, to ensure a consistent quality of review. This should be a good start; we can add to it in the future as we discover more things that we'd like reviewers to consider. As a side effect, attempt to enumerate the bits of C99 that we rely on, past the C89 baseline that we otherwise assume. Change-Id: I047cc0accc99d2aea7bb7eb1c35ae148bff2ee7d Reviewed-on: https://gerrit.openafs.org/15611 Reviewed-by: Michael Meffie <mmeffie@sinenomine.net> Reviewed-by: Andrew Deason <adeason@sinenomine.net> Reviewed-by: Mark Vitale <mvitale@sinenomine.net> Reviewed-by: Cheyenne Wills <cwills@sinenomine.net> Reviewed-by: Benjamin Kaduk <kaduk@mit.edu> Tested-by: Benjamin Kaduk <kaduk@mit.edu>
This commit is contained in:
parent
e79985dbf2
commit
a27728daff
190
CODING
190
CODING
@ -271,6 +271,196 @@ change. One workflow to do this:
|
||||
3) Run "git commit --amend", saving and quitting again. Git will run
|
||||
the commit hook and generate a new Change-ID for Gerrit.
|
||||
|
||||
Code Review Guidelines
|
||||
======================
|
||||
|
||||
OpenAFS's use of Gerrit imposes a requirement that all changes undergo
|
||||
code review prior to being merged to any branch, whether it be master or
|
||||
a stable release branch. The following guidelines discuss what the
|
||||
scope of that review is expected to cover. All of the points need to be
|
||||
addressed by some reviewer, though not necessarily by the same reviewer.
|
||||
When doing a review that only addresses some of these topics, indicate
|
||||
in the review comment which portions were/were not done.
|
||||
|
||||
What does the change claim to do?
|
||||
---------------------------------
|
||||
|
||||
This is often taken for granted, but in order to review a change we have
|
||||
to know what it is trying to do (so that we can tell if it successfully
|
||||
does so). In our Gerrit-based workflow, this should be in the commit
|
||||
message itself, but a patch attached to a bug or email may not have that
|
||||
additional context. If the change is fixing a bug, the bug report can
|
||||
be expected to give some description of the issue being fixed. In
|
||||
addition to the sense of "what" is expected to change, a reviewer also
|
||||
expects to know the "why" behind the change. This becomes important
|
||||
when assessing if this change is the right thing to do (see below).
|
||||
|
||||
At this part of the review, it's enough to ensure that the what and why
|
||||
are documented and seem plausible.
|
||||
|
||||
Does the change do what it claims to do?
|
||||
----------------------------------------
|
||||
|
||||
With the newfound knowledge of the goal of the change, it becomes possible
|
||||
to assess whether it achieves that goal. Follow through the "normal" or
|
||||
"success" path of the new/changed code and scrutinize what it _actually_
|
||||
does. As you do this, strive to discern differences between the actual
|
||||
behavior and the intended behavior -- does it live up to what was
|
||||
advertised?
|
||||
|
||||
If the actual changes have more things than the stated goal, that may be
|
||||
a sign that there are unrelated changes included -- it is often best to
|
||||
ask to split these into a separate commit (but if not, they should be
|
||||
acknowledged in the commit message as intended inclusions). It is also
|
||||
okay to ask for mechanical or similarly repetitive changes to be broken
|
||||
out into dedicated commits (so that the reviewer would just
|
||||
independently reproduce the mechanical change and compare the resulting
|
||||
tree).
|
||||
|
||||
Having looked at the normal path, it's then time to look at the
|
||||
not-normal path. Error cases, edge cases, look for these wherever you
|
||||
can and see if the code is going to do the right thing in those
|
||||
scenarios. Try to spot any copy/paste issues in this phase as well --
|
||||
if we are doing conceptually the same thing in more than one instance,
|
||||
check that each instance is actually touching the fields appropriate for
|
||||
that instance. Sometimes the difference is just a single character, and
|
||||
it's easy to miss (sometimes the copy/paste has been cleaned up once on
|
||||
a given line but needed to be cleaned up twice). Is there a cleanup
|
||||
handler that should be jumped to? Locks or other state that need to be
|
||||
released? It is generally reasonable to assume that the adjacent error
|
||||
cases/early returns are correct, and only examine what has changed
|
||||
between that call site and the one you're looking at. Check for edge
|
||||
cases -- if there are conditionals against a specific constant, verify
|
||||
the expected behavior when the input is that value/length, as well as
|
||||
just above and just below. This holds especially for string lengths
|
||||
(e.g., in PRNAMEs the length bound need not account for a trailing NUL)
|
||||
but checking for edge cases is always important.
|
||||
|
||||
Does the change not do something it should be doing?
|
||||
----------------------------------------------------
|
||||
|
||||
If the goal of the change is to be comprehensive about doing something
|
||||
(within a given scope), check that it has actually changed all the
|
||||
places that need to change. It can take a bit of creativity to be able
|
||||
to write an efficient check for this -- sometimes just searching on the
|
||||
API name is sufficient, but maybe there is a specific constant (for port
|
||||
number, AFS service number, a given data field's length, etc.) that
|
||||
would apply, This is probably going to require a lot of manual
|
||||
inspection; `${PAGER} $(git grep -l ${PATTERN})` followed by a search in
|
||||
each file to find the relevant instance(s) is a useful technique.
|
||||
|
||||
Do NOT just rely on the compiler to detect incompatible/missing APIs --
|
||||
we have code that is conditionally compiled based on architecture and
|
||||
environment, and we also want to ensure that we find relevant comments,
|
||||
tests, and documentation that might be exercising the interface in
|
||||
question.
|
||||
|
||||
Check for comments/documentation for the affected areas of the code.
|
||||
Are there any comments that have become stale and need to be changed,
|
||||
but are not changed? Unless the goal of the change is to fix a bug and
|
||||
restore the documented behavior, if the behavior is changing, we should
|
||||
expect the documentation (if present) to be changing as well. (If there
|
||||
is no documentation, consider adding some.)
|
||||
|
||||
Are there related functions or logic that should be getting a similar
|
||||
change to it? See above about conditionally compiled code -- sometimes
|
||||
we have multiple implementations of a given API depending on the
|
||||
environment or architecture, and often (but not always) when we need to
|
||||
change one, we need to change others. A network API may have multiple
|
||||
versions as it has evolved over time; learn about the intended
|
||||
differences between generations of the API to determine which one(s)
|
||||
should be getting a given change. If fixing a bug (especially when
|
||||
security-relevant), try to generalize or abstract the nature of the fix
|
||||
to see if there are other places where an analogous fix would apply.
|
||||
Returning uninitialized data to a network caller is the classic example
|
||||
here, but this can arise in many ways.
|
||||
|
||||
Does the change not do what it claims to not do?
|
||||
------------------------------------------------
|
||||
|
||||
If there is supposed to be no functional change, is that true?
|
||||
If a given class of behavior/logic is supposed to be unmodified, trace
|
||||
through the code and confirm that that's the case.
|
||||
|
||||
Is that the right thing to do?
|
||||
------------------------------
|
||||
|
||||
This question is a bit open-ended and sometimes hard to apply in
|
||||
practice, but it's usually worth taking a step back and thinking about
|
||||
what problem the change is trying to solve, whether it could be solved
|
||||
some other way, and whether solving a different problem would be more
|
||||
appropriate. For example, rather than trying to optimize an existing
|
||||
API at the cost of very convoluted logic, maybe there is a simpler API
|
||||
or different data structure to achieve the overall goal that has a
|
||||
different overall structure. Contrariwise, though, changing the
|
||||
behavior of a network API is pretty complicated to get right, and a
|
||||
local adjustment may be more worthwhile.
|
||||
|
||||
Style and Standard Compliance
|
||||
-----------------------------
|
||||
|
||||
This is sometimes done in passing during an earlier stage of the review,
|
||||
but always make a point to consider whether the changes conform to the
|
||||
project's style guidelines. New code should conform to the project (or
|
||||
prevailing) style, and generally we should fix the style when making
|
||||
substantive changes to a given line. Often we will fix style of
|
||||
adjacent lines as well, if there are not too many, but it's a judgement
|
||||
call -- if the focus of the change becomes more about style than
|
||||
bugfixes, it's probably time for a separate cleanup commit. It's also
|
||||
important to think about whether the change conforms to the language
|
||||
specification; we officially target C89 plus a handful of extensions
|
||||
(though it may be simpler to reason about it as C99 minus a handful of
|
||||
features). The precise list of extensions over C89 that we use is a
|
||||
work in progress, but includes (and will be updated as encountered):
|
||||
- the long long int type
|
||||
- designated initializers
|
||||
- the __func__ predefined macro
|
||||
If we're using additional language extensions or more modern
|
||||
features, they need to be scoped to a file/situation where we can
|
||||
guarantee they are available.
|
||||
|
||||
We must always try to avoid undefined behavior (though there are
|
||||
existing cases where we do potentially trigger it).
|
||||
|
||||
Our network and userspace/kernel APIs need to conform to any relevant
|
||||
standards or specifications that apply. We cannot unilaterally change
|
||||
existing RPC-L (the .xg files processed by rxgen), and the scope for
|
||||
adding new things is limited. When we add new pioctls, they have to
|
||||
come from the OpenAFS-specific range (i.e., be 'O' pioctls). Com_err
|
||||
codes in .et files are generally managed by the central.org registrar,
|
||||
etc.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
New APIs should get documented via at least doxygen comments attached to
|
||||
their implementation. Changes to CLI commands require man page changes.
|
||||
New public APIs should also get man pages. The reviewer should check
|
||||
that these are being done.
|
||||
|
||||
If the change is modifying the behavior of existing functionality that
|
||||
has documentation, update the documentation accordingly. (Don't forget
|
||||
the HISTORY section of man pages!) If there is not existing
|
||||
documentation, the code review is a good opportunity to ask if
|
||||
documentation should be added, but the answer to that question will vary
|
||||
on the specifics of the case in question.
|
||||
|
||||
Test Coverage
|
||||
-------------
|
||||
|
||||
The current test coverage in the tree is very sparse. If adding to or
|
||||
changing the behavior of an area of the code that does have some test
|
||||
coverage, add tests for the new/changed behavior. If fixing a bug, add
|
||||
a regression test. It is a good idea to confirm that the (new) test
|
||||
fails with the code change reverted, though sometimes this is
|
||||
complicated enough to be an unreasonable burden on the reviewer.
|
||||
|
||||
For areas of the tree that do not have existing code coverage, we should
|
||||
consider adding some. It's good for the reviewer to ask (and consider
|
||||
themself) what level of effort would be involved in adding test coverage
|
||||
for this part of the code and whether we should put that effort in at
|
||||
this time. The answer may be "not worth it right now", though.
|
||||
|
||||
Warnings
|
||||
========
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user