"Comments Considered Evil"

What do people think of this?

I’m about 90% in agreement with it. One of my biggest gripes in modern trends is docblocks above method headers that do nothing but repeat information available in the method header. Repeating the argument names is a horrible waste of time.

A high percentage of comments are useless. As a simple experiment I looked at a common and popular component: Symfony HTTP Kernel to look at the comments:

The header’s fine… but look at this:


    /**
     * Constructor.
     *
     * @param EventDispatcherInterface    $dispatcher   An EventDispatcherInterface instance
     * @param ControllerResolverInterface $resolver     A ControllerResolverInterface instance
     * @param RequestStack                $requestStack A stack for master/sub requests
     *
     * @api
     */
    public function __construct(EventDispatcherInterface $dispatcher, ControllerResolverInterface $resolver, RequestStack $requestStack = null)
    {
        $this->dispatcher = $dispatcher;
        $this->resolver = $resolver;
        $this->requestStack = $requestStack ?: new RequestStack();
    }

… “Constructor”, 100% useless.

     * @param EventDispatcherInterface    $dispatcher   An EventDispatcherInterface instance
     * @param ControllerResolverInterface $resolver     A ControllerResolverInterface instance

This tells us nothing that isn’t in the method header.

     * @param RequestStack                $requestStack A stack for master/sub requests

Gives us some information that isn’t in the method header but it’s not particularly useful.

    /**
     * {@inheritdoc}
     *
     * @api
     */

Absolutely useless for anyone looking at the code. More useful for an IDE, perhaps, but an IDE should probably inherit docs from the interface anyway. 100% pointless.

    /**
     * @throws \LogicException If the request stack is empty
     *
     * @internal
     */
    public function terminateWithException(\Exception $exception)
    {

Finally a helpful comment: We know that it can throw an exception, something we can’t see from the method header… No description of what the method actually does though.

    /**
     * Handles a request to convert it to a response.
     *
     * Exceptions are not caught.
     *
     * @param Request $request A Request instance
     * @param int     $type    The type of the request (one of HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST)
     *
     * @return Response A Response instance
     *
     * @throws \LogicException       If one of the listener does not behave as expected
     * @throws NotFoundHttpException When controller cannot be found
     */
    private function handleRaw(Request $request, $type = self::MASTER_REQUEST)

A mixed bag here. @param mostly repeated information from the method header. Listing available constants for the $type param is helpful, although not very useful without explaining the difference between the types, to work that out I need to look through the code anyway.

     // request
        $event = new GetResponseEvent($this, $request, $type);
        $this->dispatcher->dispatch(KernelEvents::REQUEST, $event);

What is //request adding here? If anything it’s confusing. What is it referring to? There is a variable called $request but it seems unrelated to the comment. Removing the comment would not change the understandability of this code

    // load controller
        if (false === $controller = $this->resolver->getController($request)) 

is there a difference between get and load? If so, either the comment or method name is wrong, if not, the comment is irrelevant.

// controller arguments
        $arguments = $this->resolver->getArguments($request, $controller);

Arguably useful, although probably not given the aptly named variables.

        // call controller
        $response = call_user_func_array($controller, $arguments);

100% pointless comment.

        // view
        if (!$response instanceof Response) {
            $event = new GetResponseForControllerResultEvent($this, $request, $type, $response);
            $this->dispatcher->dispatch(KernelEvents::VIEW, $event);

//view unhelpful, adds nothing of use at all.

   // the user may have forgotten to return something
                if (null === $response) {
                    $msg .= ' Did you forget to add a return statement somewhere in your controller?';
                }
                throw new \LogicException($msg);

This is well explained by the exception message, I’m not sure the comment adds anything here.

 /**
     * Filters a response object.
     *
     * @param Response $response A Response instance
     * @param Request  $request  An error message in case the response is not a Response object
     * @param int      $type     The type of the request (one of HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST)
     *
     * @return Response The filtered Response instance
     *
     * @throws \RuntimeException if the passed object is not a Response instance
     */
    private function filterResponse(Response $response, Request $request, $type)
    {

filters a response object. We can see that from the header, the description does not explain what the function actually does. Filters it in what way?

* @param Request  $request  An error message in case the response is not a Response object

This is at best confusing… an error message is generally a string, why is this a Request object?

     * @param int      $type     The type of the request (one of HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST)

Again, explaining the options is useful, but no detail on what they actually do.

I’m not going to go through the rest of the class but it continues in this vein. Most of the comments either add nothing or repeat information that is in the method header. There are a few arguably useful comments but they are far outnumbered by pointless ones.

Interestingly, the most ambiguously named method does not have any comments at all:

``php

}

private function varToString($var)
{

Just to be clear, I'm not picking on this class/project/author, I just wanted to use it as a case-study and look at how comments are actually used in the wild. 

It was also the first class I looked at so may not be representative, but it does mirror my own experiences and reinforce my (mostly) agreement with the article I linked to.

With PHPMetrics and other tools using % of comment lines as an important metric, it just seems these comments exist to improve the score under analysis without actually helping developers looking at the code.

Interesting title. Are you click baiting like Tony now? LOL! :smiley: …j/k

This is what the documentation of that class looks like generated for public viewing of the class’ api.

http://api.symfony.com/2.7/Symfony/Component/HttpKernel/HttpKernel.html#method_handle

One could also question, whether or not the commenting in HttpKernel class is enough or proper for this purpose too.

PHP being the open source (i.e. not compiled) type of language it is, it is rare the code is not also viewable. The API documentation is sort of superfluous. But, certainly, this kind of documentation could be useful for code actually hidden from/ not accessible for the client developer?

Being the intermediate beginner that I am, I am finding commenting code a pain in the ass. I am constantly thinking, what am I supposed to write here, that isn’t already clear in the code?

If, however, I am thinking about external documentation, like the api docs above, I’d basically have to explain exactly what the intentions are in detail, which is like double data entry (and a waste of time). Because the code isn’t available in such external documentation, I need the extra explanation (and double work). That is the only reason I can come up with for doing any form of serious commenting.

At least I only need the extra detailed explanations for the public and protected attributes and methods. Privately set attributes and methods don’t need the extra documentation, since they should be hidden anyway and don’t get published in the api docs anyway. :smile:

I’d also say, commenting is good, when you aren’t sure yourself about how the logic is doing what it is doing or if you feel the code could be better or you’ve got that feeling it is fragile or lacks enough thought, etc. etc. In other words, when you are kludging, then comment like hell. LOL! :smiley:

Scott

Actually, I think this is something me and Tony agree on (mostly) http://www.tonymarston.net/php-mysql/minimalist-approach-to-oop-with-php.html#docblocks although I do think they’re useful when they actually add something which isn’t already obvious from the method header.

Indeed, and that is somewhat useful, but if you need to put in @param for PHPDocumentor to recognise that a parameter requires a specific type, it’s a flaw in PHPDocumentor rather than the comments. There’s no reason that PHPDocumentor cannot determine this:


Parameters
EventDispatcherInterface	$dispatcher	An EventDispatcherInterface instance
ControllerResolverInterface	$resolver	A ControllerResolverInterface instance

without the docblocks.

Oh don’t get me wrong, I don’t think all comments are bad, but most of them add nothing of value to someone looking at the code.

Personally I’d rather see comments explaining what a function does in the Unit Test rather than the class itself, that way I have both a description and example usage in one place.

Edit: Wouldn’t it be nice if PHPDocumentor looked at the unit tests rather than the classes and then generated docs with the descriptions and some usage examples from the test cases.

Hehe…although not my point, point taken. I was alluding to Tony’s “DI is evil” title and him actually using DI and the title just an attention getter more than what the article is about.

And that is an interesting idea. Could a documentation automation system properly gather the right information out of unit tests to document the class? I am thinking yes, but from my experience, (which I have little to go on), I am not certain.

Scott

If I get time I might throw together a proof of concept generator, but I’d envisage it looking something like this. Class:

<?php
namespace Maphper\Lib;
//Allows reading/writing of properties on objects ignoring their visibility
class VisibilityOverride {
	private $readClosure;
	private $writeClosure;

	public function __construct() {

		$this->readClosure = function() {
			$data = new \stdClass;
			foreach ($this as $k => $v)	{
				$data->$k = $v;
			}
			return $data;
		};

		$this->writeClosure = function($key, $value) {
			$this->$key = $value;
		};
	}

	public function getProperties($obj) {
		$read = $this->readClosure->bindTo($obj, $obj);
		return $read();
	}

	public function writeProperty($obj, $name, $value) {
		$write = $this->writeClosure->bindTo($obj, $obj);
		$write($nane, $value);
	}
}

Then an annotated test:

```php
/*
@example 1
*/
class MyClass {
	private $property1 = 1;
	private $property2 = 2;
}

/*
@class Maphper\Lib\VisibilityOverride;
*/
class VisibilityOverrideGetPropertiesTest extends PHPUnit_Framework_TestCase {
	/*
	@method getProperties
	@description returns the properties of $object ignoring their visibility
	@param The object to read the values from
	@example 1
	*/
	public function testPropertyGet() {
		$visibilityOverrider = new VisibilityOverride();
		$properties = $visibilityOverrider->getProperties(new MyClass);

		$this->assertEquals($properties->property1, 1);
		$this->assertEquals($properties->property2, 2);
	}
}

Which could then generate the docs from the test case, including the example. The example code could even be automatically rewritten slightly:

Example 1:

class MyClass {
	private $property1 = 1;
	private $property2 = 2;
}

$visibilityOverrider = new VisibilityOverride();
$properties = $visibilityOverrider->getProperties(new MyClass);

var_dump($properties->property1 == 1); //TRUE
var_dump($properties->property2 == 2); //TRUE

Obviously there would be some considerations for when you had multiple tests for the same method: Where do you put the description (The first test, I guess?)… Mocks could also be rewritten to actual instances with values and such.

Well not 100%, for the reason you’ve just specified. IDE inheritance and validation (hey, when Coder Magee invokes this function 6 scripts away from now, what parameters do they have to have? What helptext can we display? How do we verify if a change to this script borks 200 other lines of code because you added a parameter?)

I worked for a while in a full Microsoft deployment - C# in Visual Studio, hooked into StyleCop and MS’s own TFS, code validation and test servers And you HAD to have all of the incoming and outgoing variables declared. You HAD to have a multi-word description for each function (it even had to end in a period!)

Except you and I both know that variable names aren’t always clear - I can call my variable $squonk, but if i comment it in the header, you know exactly what $squonk is, it’s expected type, and it’s use. Sure, it’d be nice if everything were nice and neat and variable names all made sense, but at least with a comment I can read Coder Twit’s header and know what the hell $squiggle is supposed to be without diving into how it was used.

Overall, I don’t think your problem is with the existence of comments (which, btw, in my case were forced to exist by the IDE/TFS in order to pass acceptability), but with the quality thereof.

You’re absolutely right, if @param entries actually included information that wasn’t already in the method header (the class type and variable name), then there would be some point in them, however a lot (I’d go as far as to say a sizeable majority) of @param tags I see offer exactly zero information which isn’t already there wasting my time as a developer reading them, someone’s time writing them (or auto-generating them) and wasting bytes.

“Considered evil” essays considered evil. :wink:

It’s very important that you both pointed those out. Docblock comments are primarily meant for machine consumption, not human consumption. They let us generate documentation or IDE hints.

Granted. Nonetheless, it is what it is, and we need to write our docblocks in such a way that tools like PHPDocumentor will produce the output we want.

1 Like

That’s a really good argument for doing away with comments completely.

If you call your variable $squonk then to make it clear you need to comment what it is every single time it is referenced and not just in the header. Otherwise people may not know what it is as why should you need to read the comments in the header when you are reading the code. If they ca’t define meaningless variable names in comments then they would have to use meaningful variable names.

Anyway meaningless variable names should never get past code review.

It is annoyingly sensationalist, especially for a journal article, but it does raise some very valid points. A good proportion of comments are at best redundant.

…which are for human consumption. Whatever you’re using the docblocks for, eventually they are there to give developers help somehow.

Oh…I take my bit of kidding back. I didn’t realize the journal article was titled the same. LOL! :smile:

Scott

Well I agree with the points made by TomB, although I feel it’s not that comments themselves are evil, but the developers who dont know how to write them properly. It’s the developers who need to improve, and to write more meaningful comments rather than repeat of their code. And of course, PHPDocumentor aint perfect and it creates some problems for developers.

With that being said, my primary issue with Symfony’s comments is that they are used as annotation, while PHP annotation is abomination. I know some people like this, but I for one firmly believe that comments should just be ‘comments’, not some form of black magic that alter the code’s behavior, an application that requires docblock comments to run properly is just plainly wrong.

3 Likes

The article title is too sensationalist :slight_smile: Saying that comments are evil is far too extreme, however I agree that redundant comments are bad practice and descriptive function and variable names are better. However, there are cases where descriptions are necessary to make the code clear.

I fully agree with this reasoning, however I think for the time being this is the necessary consequence of PHP having its legacy and lack of features. phpDocumentor has been for a while and offered a way to annotate argument types but type hinting is still a new thing in PHP and still not complete so it’s not possible to infer every type from argument declarations. I think having some arguments annotated and some not is not consistent and would be worse for readability than just annotating all arguments and having type hints repeated. Repetition is never elegant but in this case it’s an acceptable compromise.

Since I started using phpDocumentor annotations I’ve found my code to be more readable. Most IDEs will auto-generate them instantly so the effort is almost none. If I don’t feel I need any descriptions I just generate annotations with argument types and leave them like this. This has the advantage of coming back to the code later on and being able to add any description easily because I already have the place to add it. For me the benefits in this type of consistency outweigh the ugliness of repetition and redundancy - which is not really ugliness since I always set my IDE to display comments in bright magenta so they nicely stand out from the code and in the worst case (when they don’t describe anything that’s not in the code) act like visible delimiters between method declarations.

BTW, Tony, when reading the code of your DICE a while ago I wished every method with all its in and out arguments had been annotated as I found it hard to understand quickly what each method specifically does - and I really wouldn’t mind if some of the annotations were redundant :smiley:

Interesting. I do the complete opposite. I set the comments in the background, so they don’t distract from the code.

Could you post a screenshot of your IDE here? :smile: Would be interesting to see what such highlighted comments look like. :wink:

Ooh. His name is Tom. Calling him Tony is probably the best insult you could ever say to him. Hahaha! Aint that right Tom? :smiley:

Scott

I am aware most people keep comments in pale font. I have tried both ways and after a while I’ve always come back to bright colours. Comments may seem to be less important because they are not executed but I am of the opinion that comments are important to the programmer so keeping them visible makes sense. Maybe this even keeps me from writing redundant comments because there would be too much magenta on the screen :slight_smile: . Here it is:

https://dl.dropboxusercontent.com/u/13230070/screenshots/ide.png

Of course this is purely subjective, just a personal preference. A few months ago I took to writing a mobile app in Android Studio - a different IDE and I kept the default grey comments just to see if maybe they would make more sense to me. After a month I changed them to magenta and immediately felt improvement in code readability :smile:

Oh, indeed! I know he is Tom, I just made a typo, I don’t know why… Unfortunately, I can’t edit my post, so Tom please accept my apologies :sunny:

1 Like

Actually, the contrast to white with magenta seems to make the comments meld a bit into the background or rather the code still stands out well. So, yeah. It could work for me too, if I used a white background, which I don’t. It is scientifically proven that working with a white background is harder on the eyes and I also notice it too, working on other text content, like in Word or Markdown editor, the white background makes my eyes tired faster.

Ok. Back on topic. Comments are like guns. They are only bad, when misused or used improperly. The better question is, what is a proper comment? I looked up comments in PHP the right way and the only mention is documenting code according to the informal PHPDoc standard.

Scott

You are right - the comments meld into the background but are still quite visible, more than grey, which means the sweet spot for me.

Yeah, I’ll have to try it. The problem for me is that while I could make my IDE dark it’s difficult to make other programs dark and I don’t like the high contrast of having one application dark and another light, so I prefer to keep everything light and simply turn down monitor brightness. And I think the tiredness has a lot to do with display quality - after all there is no problem reading white books in natural light. Harsh and unnatural light coming from the monitor certainly is not healthy and then dark backgrounds help because we reduce the overall amount of artificial light coming from it.

To me this is simple: first, make the code self-explanatory by using proper names, logical division into methods, etc. When something is not fully clear from the code alone - add a comment. I just imagine if the code would be clear for an outside programmer - if not, then it’s a good place to add a comment. Very often the outside programmer is me after a few months or years and then I’m grateful to myself for commenting :slight_smile:

Ideally the code itself should be clear enough to understand what it does.

It should also be unnecessary most of the time to explain how it does what it does.

What always needs commenting is why a piece of code uses a less obvious approach than might normally be expected - such as when a small piece of code needs to process a huge number of transactions where making the code clear and easy to read also makes it very slow and inefficient and so the code has been rewritten in a less obvious way in order to save hours of processing time.

What I find often missing with documentation, and this is a bit off-topic from code commenting, is not how the code specifically works, but the overarching concepts the design decisions are trying to cover. Tom’s Dice project is a great example of what I mean and how to do it correctly. With Dice he has a 100 line class for dependency injection, but has written practically half a book about what it is all about and how it is useful. To me, this is what marketing is actually all about. So, I’d say, proper marketing of a work of code, in the sense of telling its greater story, is incredibly important, in addition to decent code comments.

Scott

1 Like

That’s documentation rather than comments. All projects should have several different documents associated with them aimed at the different groups involved - technical specifications, user documents etc. Proper documentation can easily run to somewhere between 10 & 100 times the number of lines of code.