Hi,
2014-09-11 10:43 GMT+02:00 Caleb James DeLisle <cjd(a)cjdns.fr>fr>:
Hi Jeremie,
Sorry for not replying sooner, I felt that I should give your mail a bit
more thought and a little but more background about me since we don't know
eachother as well as I know Sergiu and Marius.
No problem and I hesitated before answering, because I didn't want to feel
harsh or misunderstood.
On 09/09/2014 06:02 PM, Jeremie BOUSQUET wrote:
Hi,
My own humble remarks ... :)
2014-09-09 11:00 GMT+02:00 Caleb James DeLisle <cjd(a)cjdns.fr>fr>:
Firstly, thank you very much for reviewing my
code.
When I was young I hated homework, my parents told me that if I spent
as much time on the work as I spent bellyaching about it, I'd have it
done already. A lot of your review seems to take the point of view that
it's "not that hard", my counter is that no rule makes contributing
*easier* and as contributors are not exactly lining up at the door, any
rule that exists needs to justify itself as necessary.
There's the implicit idea here that a rule is something created to bother
developers. Something that limit contribution, is costly eventually,
requires an effort, and so on ...
I think the concept and objectives of such rules is exactly the contrary,
they're here to help contribution (by making the code clearer), and give
a
framework that everyone knows and understands
(less effort to dig into
code
you didn't write).
Although in this mail thread I have been very critical, I should probably
show my credentials as "pro best practice". I'm a leader of a project in C
and I developed my own codestyle validator which checks, among other
things,
that all publicly accessible functions are prefixed with the name of the
file where they're defined.
Although I have on occasion wanted to violate this rule, I have yet to find
what I think is a *valid* reason for doing so and I have so far kept it
100%
which makes reading the code much easier for other people who upon seeing
a function which they don't recognize, need only scroll to the top of the
file to see which header it's defined in.
Thanks for the background, I didn't doubt about the fact that you care
about the rules, because you wouldn't have sent such email if it weren't
the case ;-)
But it was the "feeling" I had by reading your text.
On my side I've worked as developer for some time now, and along with this
had for years a role of "rule enforcer", particularly regarding
configuration management tools and processes, but also regarding the
build...
My approach has always been to explain things to justify chosen rules
though, not to enforce a dictatorship (as is sometimes the case in private
companies...).
The discussions I read here about software engineering (to which I
sometimes contribute modestly), are a breath of air for me I must say :)
Of course there are always rules that can be
discussed, regarding their
pertinence, criticality etc.
That's what I'm after, I feel that every rule imparts a certain amount of
cost and we need to periodically review them to make sure that they are
net-positive value.
That's very sane.
>
>
>
> On 09/08/2014 07:30 PM, Sergiu Dumitriu wrote:
>> I'm gonna audit your audit audit.
>>
>> On 09/08/2014 05:14 AM, Caleb James DeLisle wrote:
>>
>>>
>
/src/main/java/org.xwiki.contrib.websocket/internal/DefaultWebSocketConfig.java:26:
> Missing a Javadoc comment.
>>> Missing javadoc on a class... whose behavior is very obvious.
>>> I'm not here to teach programming.
>>
>> Once again, I don't like "DefaultXYZ" as a name for components.
Either
>> only one implementation will ever make sense, in which case it probably
>> shouldn't be an interface+implementation, or it would make sense to
have
>> alternate implementations. In the latter
case, there's nothing
"default"
This is the first time I've heard that. Indeed I don't like "Default"
either but it seems we don't use "Impl". This question brings up a more
difficult point, why do we need an interface for each thing we register
in the CM, even if there's only ever going to be one impl?
The issue I see here, is answering the question : "only one
implementation
will ever make sense". Other contributors
may invent over implementations
you never thought about.
I've been here myself, I have made a lot of very nice interfaces to
"future proof" my designs, and then when a new use case actually came
along,
I found my interfaces unduely difficult to use for that use case and I
promptly re-factored the code.
Since then, I've developed a strong tendency toward YAGNI and
"simplest thing that could possibly work". By the time someone finds a new
use case, the code probably needs some refactoring anyway.
I quite agree with you, in this specific case my point is that extracting
an interface is not a very high cost.
It doesn't mean that you have to code complex things to generalize all your
code based on those interfaces. It's just the level-0 of something that
could potentially be re-used - and if it's never re-used, which is
probable, then it didn't cost much.
Personnally, as I'm not very smart in some ways, and my memory is
frequently faulty, those interfaces are useful first to me, so I remember
what was the contract I was trying to implement ;)
I didn't apply simplicity (or didn't succeed to) for some parts of the mail
archive I code, that I've rewritten recently, and I now find those parts
totally horrible :D
"do what I say not what I do ..."
But I think it's a discussion that's a bit far from a pure coding rules
checking, it's more an architecture thing.
Following IOC and java interfaces concepts, any
component should have a
contract - even if there will be only one, ever.
This makes sense for public components but the case I'm describing is a
component inside of a /internal/ directory so it's really a
"contract with myself".
In my mind, it was not just a discussion for coding rules of "public"
things.
It's not just for people that would like to use those methods (not needed
they're private), but also for people wanting to contribute to this code.
If it's deadly obvious from the code what it does, then I agree with you,
javadoc, comments and interfaces are less needed, but it's very often very
obvious only to the person who wrote the code.
Of course it may be a different approach depending on supposed skills of
development teams. I won't talk about my work here :D
Question is more if you are willing to allow this
code to be replaced by
another implementation or not, more than if it makes sense or not at the
time being, IMHO, because you don't want thousands of components
everywhere
obviously.
>
>
>> about the implementation, other than it is used in the XWiki Platform
as
>> the default implementation. The name
should mention what's specific to
>> the implementation, and so should the javadoc:
>>
>> /** Implementation for the {@link WebSocketConfig} role which allows
>> configuring the {@link XWikiWebSocketScriptService websocket service}
>> through the usual XWiki configuration places: space preferences, wiki
>> preferences, and {@code xwiki.properties}. */
>>
>> public class WikiWebSocketConfig
>
>
> Although I'm tempted now to copy/paste your comment into the code, I'm
> not hesitant because this is not the XWiki Configuration infra, just one
> module's config.
>
> If we hardcoded "xwiki.properties" all over the code, that would be well
> accepted as a disaster, why then is it ok to hardcode documentation
about
> how XWiki's configuration works all over
the codebase?
>
> This class uses ConfigurationSource, whatever that might do.
>
>
>>
>>>
>
/src/main/java/org.xwiki.contrib.websocket/internal/WebSocketConfig.java:55:
> Expected an @return tag.
>>>
>>> I actually wrote javadoc on all of these, without even being prodded
to
do so.
> Do you think my freeform documentation is
less useful to a reader than
it would be if it contained
> a @return tag?
If I want to know what's the return value I will only check the return
description in javadoc, maybe not the sometimes length full description
above of it ...
As much as I love xwiki, I must say I'm not a bit fan of old core javadoc
;-)
Take these ones for example:
http://nexus.xwiki.org/nexus/service/local/repositories/releases/archive/or…
http://nexus.xwiki.org/nexus/service/local/repositories/releases/archive/or…
I dare any contributor to find what they do
without looking at the code
:)
As a result of the discussion with Sergiu, I have decided that public
functions should have the same javadoc standards as usual and optional
javadoc should only apply to private or /internal/ (inside of a directory
called internal) classes and functions.
>>>
>>> I usually describe the meaning of the return value in the method
>>> javadoc, and the format / possible values / special cases in the
@return
>> javadoc:
>>
>> /**
>> * The filename of the SSL private key in OpenSSL PEM format.
>> *
>> * @return an absolute path, or {@code null} if no certificate is
>> * configured
>> */
>> String getPrivateKeyFilename();
>>
>> (Why doesn't this return a File, or an InputStream, since that's safer
>> than a raw path?)
>
>
> Business logic in the configuration?
> Should the configuration also be responsible for checking if the file
> exists?
>
>
>>
>>>
>
/src/main/java/org.xwiki.contrib.websocket/internal/XWikiWebSocketScriptService.java:45:
> Missing a Javadoc comment.
>>>
>
/src/main/java/org.xwiki.contrib.websocket/internal/XWikiWebSocketScriptService.java:50:5:
> Missing a Javadoc comment.
>>>
>
/src/main/java/org.xwiki.contrib.websocket/internal/XWikiWebSocketScriptService.java:100:5:
> Missing a Javadoc comment.
>>>
>>> Do you really think that a 3 line function called getUser() needs a
> javadoc comment?
>>
>> I think that getUser doesn't belong here at all. Since it just returns
>
>
> Skirting the question.
> Do you really think that a 3 line function called
> somethingOtherThanGetUser() needs a javadoc comment?
>
>
>> the current user, why is it needed? There's already
>> $xcontext.userReference, and if you want to actually return a reference
>> to XWikiGuest instead of null, then that's wrong and deprecated.
>
>
> I do want a reference to XWikiGuest, null has a different meaning in
this
> context. There are of course many ways to
solve a given problem but if
you
>> claim that the implementation detail of how the internals of this class
>> behave are wrong and deprecated, the burden of proof is on you.
>>
> All this discussion is a proof that
it was actually needed to comment
this
! :)
On my side if I just see "XWikiWebSocketScriptService#getUser", I have
absolutely no idea of what it could return ...
- current user ?
- user of the web socket ? (if it means something)
- user that owns a thread of the web socket service ?
...
IMHO something called "getXxx" or "setXxx" is implicitely described
only
in
case of a bean or pojo, in a script service or
component interface
there's
> nothing really obvious usually ...
This all makes perfect sense and I would agree entirely if it were not for
the fact that it's a private method.
But what's the real difference ? private is a java keyword. Private methods
are as "open-sourced" as the others ;)
If I ever want to contribute a fix of a bug in this code, one day, it may
be helpful to me, to know what you intended to do when you wrote this
method. Because if a bug is found, it would mean that what you implemented
was not quite like what you intended to.
But I admit I may be a bit radical and theoretical here.
>
>
>>
>> As for the other longer methods, since this is a public API it should
be
>> documented.
>>
>> Also, it was agreed that script services should be in a non-internal
>> org.xwiki.module.script package.
>>
>
>
> That's a good point, I'll document those.
>
>
>>> I can guarantee you that the competition are not going to slow down
and
> smell the flowers.
>>>
>>>
>>>
>
/src/main/java/org.xwiki.contrib.websocket/internal/XWikiWebSocketScriptService.java:121:79:
> The String "/" appears 4 times in
the file.
>>>
>>> And it might appear 400 times, defining a static final string called
> URL_SEPERATOR is
>>> exercise for my fingers and your eyeballs, nothing more. If I really
> wanted to obfuscate
>>> my code, I could use an external library to make you go hunting for
the
value of
> YouWillNeverFindMe.SEPERATOR
We can exclude certain strings from the check. We currently exclude a
few common strings: "", "[0-9]", " ", "]". We
could add "/" as well.
This rule is supposed to protect us against poor-man's constants
copy-pasted in many places, since this is error-prone (typos) and hard
to refactor. It also forces us to define useless constants just for
working around this rule, which I agree is almost as bad. There's a
tradeoff between maintainability and convenience, and the XWiki
community agreed to go for more maintainability.
I think what's really missing here is that they never really exposed an
URL
separator constant in the JDK ...
But it's difficult for a checking-tool to differentiate between those url
separators (that will never change, so who cares as you say), and using
file paths separators hard-coded (which is pretty bad and create many
environment specific weirdness in so many tools). I think if they did
that
with maven plugins from the start, they
wouldn't be hunting for these '/'
and '\' like they do, and they'd have lighten many JIRAs ...
I think the correct solution to windows insanity is to create a filesystem
abstraction layer which deals with their their broken slashes and magical
filenames which you can never use (eg: COM1) and their
sort-of-but-not-quite
international filenames based on UTF-16 which can *almost* represent all
international characters.
If that were implemented in an abstraction layer then it would save a whole
lot of bug reports and a whole lot of pain and suffering trying to deal
with
it.
>
> It's not more maintainable, it just looks more maintainable.
> If someone's hardcoding filesystem paths inside of classes then they're
> certainly doing so many other horrible things that the file paths should
> be preserved as a red flag for reviewers!
>
> 99% of strings are not file paths, they're error messages and forward
> slashes and other things which will never change. Even configuration
> keys are impossible to change once they're deployed in the field.
>
> #define TEN 10
>
>
>>
>>>
>
/src/main/java/org.xwiki.contrib.websocket/internal/WebSocketService.java:25:
> Missing a Javadoc comment.
>>>
>
/src/main/java/org.xwiki.contrib.websocket/internal/WebSocketService.java:28:5:
> Missing a Javadoc comment.
>>>
>>> It's inside of an /internal/ directory, if you can't infer it's
meaning
>>> then either you can't read or I
can't code.
>>
>> It's a component role, but it's internal. That's a bit of a
>> contradiction, but let's ignore that for the moment.
>>
>> I looked at that interface (ignoring the JavaDoc that's there now), and
>> out of context I can't infer it's meaning.
>
>
> Because it's internal, I assume that you can just look at the
> implementation.
> But this gets back to the question of why we need to define interfaces
for
which
there will never be more than one impl.
Default scope for javadocs with maven plugin is protected ... Not public.
Personnally I (try) to put javadocs everywhere, even on private methods
(Hey, I don't succeed, you can blame me ;) )
Because I describe there the contract of the method - not how it's
implemented - there are non-javadoc comments for that.
And often I'm the first who reads them, when I'm back to work after a
long
time : "so, what was this for again ...
?"
Sometimes the name of the method is obvious, but almost all the time it's
not sufficient.
It's also a nice way to document a change - you see the change in
contract,
and impact on implementation, or you see that
implementation changed, but
not the contract.
(when I say contract here it's more than the method signature)
But that's mainly my point of view because I like javadocs ! :)
>
>>
>>>
>>>
>
/src/main/java/org.xwiki.contrib.websocket/internal/NettyWebSocket.java:33:
> Missing a Javadoc comment.
>>>
>
/src/main/java/org.xwiki.contrib.websocket/internal/NettyWebSocket.java:58:5:
> Missing a Javadoc comment.
>>>
>
/src/main/java/org.xwiki.contrib.websocket/internal/NettyWebSocket.java:59:5:
> Missing a Javadoc comment.
>>>
>
/src/main/java/org.xwiki.contrib.websocket/internal/NettyWebSocket.java:60:5:
> Missing a Javadoc comment.
>>>
>
/src/main/java/org.xwiki.contrib.websocket/internal/NettyWebSocket.java:62:5:
> Missing a Javadoc comment.
>>>
>
/src/main/java/org.xwiki.contrib.websocket/internal/NettyWebSocket.java:67:5:
> Missing a Javadoc comment.
>>>
>
/src/main/java/org.xwiki.contrib.websocket/internal/NettyWebSocket.java:69:5:
> Missing a Javadoc comment.
>>>
>
/src/main/java/org.xwiki.contrib.websocket/internal/NettyWebSocket.java:70:5:
> Missing a Javadoc comment.
>>>
>>> It's internal and this module is small, if a reasonable programmer
> needs this
>>> granularity to understand this module, it should be rewritten from
> scratch.
>>
>> Except the type, these actually are missing an @Override. As for the
> type:
>
>
> Override indeed solves the checkstyle error.
>
>
>>
>> /**
>> * {@link WebSocket} implementation using the
>> * <a href="http://netty.io/">Netty library</a>.
>> */
>
>
> Does telling the reader that Netty means netty.io make the code
> fundamentally better?
> If so then I could make all of my code better by telling the reader how
to
use
google.
Personnally I don't like those kind of external links, because in some
time
from now they could easily have disappeared,
leading to broken links
everywhere in your javadoc ...
I'd document these in the overall component/module/software
documentation,
in a wiki, in somewhere easily up-to-date.
>
>
>>
>>>
>
/src/main/java/org.xwiki.contrib.websocket/internal/NettyWebSocketService.java:118:40:
> The String "SSL enabled with
websocket.ssl.certChainFile set " appears 2
> times in the file.
>>
>> Easy fix:
>>
>> - throw new RuntimeException("SSL enabled with
>> websocket.ssl.certChainFile set " +
>> - "but the pkcs8PrivateKeyFile does not seem to
exist.");
+ throw new RuntimeException(
+ "SSL enabled with websocket.ssl.certChainFile set but
the pkcs8PrivateKeyFile does not seem to exist.");
Easy fix is still worse than "not broken", I don't want to know how easy
it is to trick this rule, I know a lot of tricks already. I want to hear
a justification for this rule in the face of the fact that it makes
development harder than it would otherwise be.
I think the fix should logically bring another broken rule about
appending
strings with '+' :D
I'm not sure where should be put the cursor, from "it makes development
harder" compared to "it's an important rule", apart from applying
rules
that were agreed among a community ...
If I ever perform a modification on this code, let's say around the first
RuntimeException, and I changed the name of the property to something
else
than "websocket.ssl.blabla", I could
maybe easily miss the fact that this
string appears somewhere else, then the user would get a different
property
name in different exceptions depending on the
use-case, one good one
wrong.
(blame me for being a bad developer not searching for this string in all
the code if you want :), but remember, I'm a contributor, not a
committer,
I won't, I believe form follows function, working code rules. But then
that's
what this thread is about :)
However the example is not so great because configuration keys are
impossible
to change in practice.
you didn't hire me on my skills, you can just
provide me as many
recommendations as you want, and I may never follow them.
Your only possibility is to review my code thoroughly (which will not
improve time for merging contributed code), or rely on some tool to
automate some basic checks.)
BTW IMO it should be the name of this property that should be in a
constant, even before this exception message string.
For me this falls in the "I might do that when I refactor that code but
it's not critical" category. I see your point.
>
>
>>>
>
/src/main/java/org.xwiki.contrib.websocket/internal/NettyWebSocketService.java:118:92:
> '+' should be on a new line.
>>>
>
/src/main/java/org.xwiki.contrib.websocket/internal/NettyWebSocketService.java:122:92:
> '+' should be on a new line.
>>>
>
/src/main/java/org.xwiki.contrib.websocket/internal/NettyWebSocketService.java:132:94:
> '+' should be on a new line.
>>>
>
/src/main/java/org.xwiki.contrib.websocket/internal/NettyWebSocketService.java:133:92:
> '+' should be on a new line.
>>>
>>> It's arguably better to put it on the line before because some
> languages (python)
>>> infer semicolons and will fail to parse if it's on the line after.
>>
>> This isn't Python code. But I agree, I don't have a good reason for
>> putting it on the next line, and I've wondered about this rule as well.
>
>
> I write code in a number of languages and I want to adopt habits which
> are as widely applicable as possible.
>
>
>>
>>>
>
/src/main/java/org.xwiki.contrib.websocket/internal/NettyWebSocketService.java:235:9:
> Executable statement count is 37 (max allowed
is 30).
>>>
>>> Statements per function is almost useless as a measure of code
> complexity.
>>> I claim negative usefulness because it is misleading.
>>
>> Would you say the same if the limit was at 5000 statements, and you
were
>> over that limit? We have to draw the line
somewhere, and it was
randomly
>> agreed to set it at 30. This might be too
low for some usecases, but in
>> general it is a good enough rule IMHO. You can use your judgment and
>> explicitly disable that rule for that function.
>>
>> But looking at the code, I think it could use some simplifying, I find
>> it rather long.
>>
>> The rule isn't just about code complexity, but the attention span of a
>> developer reading that code. How long does it take to put that whole
>> method in the brain? Can most developers even fit that much code in
>> memory? If not, then the method must be simplified / split.
>
>
> I've written functions much longer and much easier to read as well as
much
> shorter functions which, due to the
complexity of the algorithm, push my
> own mental capacity to it's limit.
>
> Surely I still have much room for improvement in code craftsmanship but
> I don't think a checkstyle failure is any more likely to make me
suddenly
> discover a clearer way to express a function
any more than a rejected
paper
> would suddenly make me a mathematician.
>
> On the contrary, fixing these types of errors will almost certainly make
> the code *worse* because the original state of the function is an
> expression
> of the mental model of the author and in response to failures, he will
> break
> it up along arbitrary lines in order to make the errors stop, but the
code
and the
programmer will not and cannot be fundamentally better than they
were 20 minutes before.
I mostly agree with you on this. My personal idea is that there should
be a
limit, but far greater than the usual or expected
limit.
But maybe overriding the rule is enough on a case per case basis ...
In my case I usually write much more statements than most developers
would
do, because I can't do another way :) I
prefer to write all little steps,
and let the compiler do its job, and it's the way I think it.
I think my code would most of the time break this rule ... :/
>
>
>>
>>>
>>>
>
/src/main/java/org.xwiki.contrib.websocket/internal/NettyWebSocketService.java:246:58:
> The String "?k=" appears 2 times in
the file.
>>>
>>> And you don't need to look anything up to understand that line of
code,
> any
>>> change would be for the worse.
>>
>> String key = StringUtils.substringAfter(uri, "?k=");
>> uri = StringUtils.substringBefore(uri, "?");
>
>
> I don't want to see the tricks, I want to see compelling justification
for
the
rule.
As any rule has exceptions, there are exceptions to the compelling
justification for a rule ...
Maybe some rules are bad, or badly specified, or there's just some rare
cases where they're more annoying than useful, but would you disable them
completely in all cases ?
My opinion is that we need to periodically review our rules,
(as well as rules which we don't have and perhaps should!)
We should ask a few questions:
How much does a rule help when it's helpful?
if the rule, for example, prevents deadlock conditions 1% of the time
then maybe we're willing to accept more pain to keep using it.
How much does it hurt when it misdiagnoses a non-problem as a problem?
Is it hard to "fix"?
Does "fixing" the code make it worse?
How often is it accurate?
Those are very difficult questions to answer, depending on the rules.
It's also difficult to define what is a code becoming "worse", because of
what you say just below about accuracy and "could be better" and so on.
How often is it inaccurate or overbearing?
This is a tricky question because often a check triggers for one
developer and not for another and the other developer is tempted
to think that his associate is wrong. We need to be careful to
differentiate "unacceptable" from "could be better" or
"not the way I would have done it".
That's a choice of what criticality to what problem, but the "not the way I
would have done it", normally already has been discussed when the rules
were decided.
It's like the law then, either you follow it, either you change it ;)
But I agree it's tricky.
By the way I understood this thread more as think-tank, but if you intended
to formalize some decisions regarding rules I apologize and will stop,
because I'm more scattering attention than focusing on solutions I'm afraid
...
Thanks,
Caleb
>
>
>>
>> (Why would any serious library use strings for representing URIs
instead
>> of java.net.URI?)
>
>
> If I gave you a string and a handful of URI manipulating functions then
> even
> if you have no knowledge of those functions, you can still understand
it as
> a plain old string, if I give you a
java.net.URI then you have to go
look
> up
> API documentation no matter what, an obvious cost increase.
>
> If there are no solid justifications for this cost then I call
> overengineering.
>
>
>>
>>>
>
/src/main/java/org.xwiki.contrib.websocket/internal/NettyWebSocketService.java:300:38:
> 'nws' hides a field.
>>>
>>> I always use 'this.' prefix when accessing object fields, I would
> prefer that
>>> as a rule but in the absence of that rule, this one is fairly
important.
>>
>> That's not an absence, the "always use this." rule is there.
>
>
> It's not enforced by checkstyle, ftr this is one rule which I would like
> to see
> added (but only if we can at least balance the development pain budget,
if
> not
> lower it).
>
>
>>
>> Having both rules is a bit redundant, since the this rule is supposed
to
>> allow the field hiding that the second
rule is complaining about.
>>
>> Still, why do you need to declare nws instead of assigning to this.nws?
>> As I can see, there's no exception that could stop the method between
>> the nwsLocal declaration and the this.nws assignment.
>>
>> And I think you just introduced a bug,
>>
>
https://github.com/xwiki-contrib/xwiki-contrib-websocket/blob/master/src/ma…
>> now references "nws" which
became this.nws instead of nwsLocal.
>>
>
> It will "probably never happen" but I fixed it for the next version,
> thanks for
> spotting that. Since it's used in an inner class, I though I needed it
to
be
final but indeed using the field works, fixed.
So to summarize, most painful rules:
1. Multiple identical string constants in a file
2. Javadoc /internal/ classes and methods
3. (not a checkstyle rule) interfaces with one impl.
4. There are 3 or 4 rules which force you to completely re-factor a
function or
class, every time one of these trips and the developer "fixes it", we're
creating
bad code.
Thanks,
Caleb
_______________________________________________
devs mailing list
devs(a)xwiki.org
http://lists.xwiki.org/mailman/listinfo/devs
_______________________________________________
devs mailing list
devs(a)xwiki.org
http://lists.xwiki.org/mailman/listinfo/devs
--
Caleb James DeLisle
XWiki SAS
calebjamesdelisle(a)xwiki.com
_______________________________________________
devs mailing list
devs(a)xwiki.org
http://lists.xwiki.org/mailman/listinfo/devs