Archive for May, 2008

No one is irreplaceable

I’m officially retiring from the symfony project core team and documentation. My recent rant about the shift in symfony philosophy lead Fabien and I to agree that we disagree. The symfony project will have to find new documentation writers.

I’ve been working on the symfony project for three years now, and it’s been a great adventure. Seeing what was originally a very specialized piece of work done by the head of a French Web Agency become one of the top web application frameworks in the whole world has just been amazing. Betting on Open-Source, dedicating time and money to develop and document an internal tool was a great move for Sensio. I learned a lot, thanks to Fabien and Sensio. I enjoyed writing the Askeet tutorial, the symfony book and all the other tutorials very much. I enjoyed writing plug-ins and applications, just for the pleasure to donate them. Now that Big Players recommend symfony for large-scale applications, a bright future opens for all the symfony developers out there.

I’ve also been battling for three years. I kept trying to put my two cents in the symfony development choices, even though I’m not a developer. I kept advocating simplicity of use over sophistication and code purity. I kept urging for smaller, more often releases. I kept giving the documentation writer’s point of view, which is that if something is hard to explain, it is probably also hard to use. I kept asking for more methods, so that there could be More Than One Way To Do Things - one simple, and one powerful. To a certain point, some of my remarks have been taken into consideration, but it required a tremendous amount of energy and stubbornness. And even now, I’m reminded that these changes (like using YAML instead of XML for schemas for instance) were mistakes.

Today, my opinions are clearly not the ones of the “symfony core team”. Important design choices are not discussed with the community, just like when symfony was only developed internally. 95% of the code base is still the result of a single man’s work and decisions. The community is just there to provide support, do the beta testing and play with plugins. This is a big strength of the project (no dispersion, no time lost in sterile discussions), but it’s also frustrating when somebody wants to get more involved.

I left Sensio last year partly because I couldn’t get my views accepted. I’m leaving symfony today for the same reason. I regret it, because I would love to continue collaborating to the project, because I really like writing documentation, and because I love the open-source spirit and the symfony community. But the time I spend on symfony is now on my free time, and I can’t just do what others decide. A hobby must be fun, not frustrating. I’m tired. Three years is long enough.

I also regret to leave a half-finished symfony 1.1 guidebook. But I heard that Sensio is writing a whole book about the new Form system (!), and I’m confident that they can dedicate internal resources to writing documentation. If you’re interested in contributing, you should get in touch with Fabien - he’s now the documentation lead (in addition to the rest).

To conclude, I’d like to quote a French guy named Talleyrand. He said: “War is much too serious a thing to be left to military men”. I personally think that Programmatic API is much too serious a thing to be left to developers.

Talking ’bout “Ma Generation”

Together with all the people working at my company, we’ve been working on a French website called “Ma Génération”. It’s a generalist news/service portal, exclusively in French - and built entirely in PHP with symfony.

Please, pay us a visit at http://www.mageneration.com.

In addition to a large custom-made CMS, the site features a forum, user blogs, user galleries, a dating service and a Netvibes-like personal homepage. Under the hood, a lot of work has been done since last August, when the development started. Some of the plugins I have been contributing lately are directly related to this website.

I want to express my thanks to all the people who’ve been working on the project, and my encouragements for the work left to be done… This is just the beginning!

Home page

Is symfony 1.1 too verbose?

Among the remarks I have about symfony 1.1, the most recurring one is the shift of philosophy between the 1.0 and 1.1 syntax. If symfony 1.0 syntax was made to write code fast, I believe it is not the case anymore with symfony 1.1, which is designed primarily for extensibility.

The result is that a symfony 1.1 application looks a lot more like a Java program. I tend to agree that Object-Orientation is a good thing because it forces you to organize your code in a modular way. But when object-orientation makes you need to keep a symfony book aside at all times and multiply the number of LOC by two, I think it's a dead end.

Disclaimer: I'm not a developer by training. I don't have much experience in web development. I started learning PHP about three years ago, and I don't think that code purity is something you should stick to. I believe in code smell, though, but that's another matter. My main interest is usability in general.

Example #1 - Tasks

Let's see how the new symfony philosophy applies to the new command line utility framework: the symfony tasks. This is a very powerful addition to symfony 1.1, made to allow an easy parsing of CLI arguments and options, and to write CLI scripts in an extensible way.

Here is a typical task initialization method, taken from sfLogRotateTask (for details about the syntax, please refer to the symfony 1.1 documentation):

protected function configure()
{
  $this->addArguments(array(
    new sfCommandArgument('application', sfCommandArgument::REQUIRED, 'The application name'),
    new sfCommandArgument('env', sfCommandArgument::REQUIRED, 'The environment name'),
  ));

  $this->addOptions(array(
    new sfCommandOption('history', null, sfCommandOption::PARAMETER_REQUIRED, 'The maximum number of old log files to keep', 10),
    new sfCommandOption('period', null, sfCommandOption::PARAMETER_REQUIRED, 'The period in days', 7),
  ));
 
  ...
}


To be able to write a task by yourself, you need to know what an sfCommandArgument is, what an sfCommandOption is, and you need to know their constants as well. That means that the number of things to learn is quite important. And why do we need to instantiate a new object to set arguments, since the only thing that addArguments() accepts is an array of sfCommandArgument objects? Because, sometime, you will take advantage of the fact that you can pass a subclass of sfCommandArgument to extend the task framework.

Developed according to the symfony 1.0 style, this would probably give:

protected function configure()
{
  $this->addArgument('application', true, 'The application name');
  $this->addArgument('env', true, 'The environment name');

  $this->addOption('history', null, true, 'The maximum number of old log files to keep', 10);
  $this->addOption('period', null, true, 'The period in days', 7);
 
  ...
}


Do you see the difference? The latter is maybe not as easy to extend, but for 90% of the cases, faster to write. Besides, the addArgument() method does not prevent you from using the long version with addArguments(), so you get both extensibility and brevity.

Note: By reading the above code, you don't know what the null and true parameters stand for. It is a matter of taste, and longer to write, but I'd rather have my method calls look like the following when the method takes many arguments. That way, every method call in your application is a useful reminder of the API.

protected function configure()
{
  $this->addArgument('application', $required = true, $help = 'The application name');
  $this->addArgument('env', $required = true, $help = 'The environment name');

  $this->addOption('history', $shortcut = null, $required = true, $help = 'The maximum number of old log files to keep', $default = 10);
  $this->addOption('period', $shortcut = null, $required = true, $help = 'The period in days', $default = 7);
 
  ...
}


Fabien added an addArgument() method to sfTask not so long ago, but it just seemed useless to him to duplicate the method signature. I think he did that just to make me stop yelling. You, developer, should learn The Right Way of Doing Things, and the right way is the longer way. If you can't understand or learn the longer way, bummer, you can't use symfony.

Example #2 - Events

The event system is another great addition to symfony 1.1. It extends the principle of mixin to allow more runtime class modifications. Once again, the symfony book is up to date on this part, so you should read the related chapter if you need to understand the syntax.

So here is how to allow a class to be extensible by way of a generic __call() in symfony 1.1:

public function __call($method, $arguments)
{
  $event = $this->dispatcher->notifyUntil(new sfEvent($this, 'foo.method_not_found', array(
    'method'    => $method,
    'arguments' => $arguments
  )));
  if (!$event->isProcessed())
  {
    throw new sfException(sprintf('Call to undefined method %s::%s.', get_class($this), $method));
  }
 
  return $event->getReturnValue();
}


And here is how you register a new bar() method with this system:

$dispatcher = sfContext::getInstance()->getEventDispatcher();
$dispatcher->connect('foo.method_not_found', array('myClass', 'listenToFooMethodNotFound'));

class myClass
{
  public function listenToFooMethodNotFound(sfEvent $event)
  {
    $parameters = $event->getParameters();
    switch($method = $parameters['method'])
    {
      case 'bar':
        $this->bar($event, $parameters['arguments'])
        return true;
      default:
        return false;
    }
  }
 
  protected static function bar($event, $arguments = array())
  {
    // ...
   
    $event->setReturnValue($result);
  }
}


As I wrote the documentation for this feature, I couldn't refrain from thinking this could be a lot easier. With symfony 1.0 philosophy, I guess you would only need to write:

public function __call($method, $arguments)
{
  return $this->dispatcher->handle($this, 'my.class.event.name', $method, $arguments);
}


And you would probably register a new method this way:

$dispatcher = sfContext::getInstance()->getEventDispatcher();
$dispatcher->addMethod('foo.method_not_found', array('myClass', 'bar'));

class myClass
{
  public function bar($object, $arguments = array())
  {
    // ...
   
    return $result;
  }
}


You can guess how the handle() and addMethod() methods of sfEventDispatcher could do all the dirty job for you. That would save you a copy/paste from the first version of the code, would be less error prone, and after all, if the program has less lines, then it's cheaper to maintain.

But, according to Fabien, that would be hardcoding the way you throw an exception in __call(), and this addition would only be useful in a very minority of cases. While the fact that you pass to notify() an sfEvent instance instead of a simple list of parameters is, once again, based on the idea that you will sometime find it convenient to subclass sfEvent. Which I think will never happen before symfony 1.2 at least.

Example #3 - Forms

Don't get me started on forms. I already wrote a post about sfForms to say that it missed an important part, and that code usability had been left aside. I still think so. Fabien doesn't.

Oh, by the way, do you know that the First Project Tutorial in symfony 1.1 showcases the new Form framework? If that doesn't discourage newcomers, I think nothing will. I wish good luck to the one who will try to make this tutorial newbie-friendly.

Conclusion

Symfony 1.1 adds a ton of wonderful features to symfony 1.0. From a technical point of view, this is a more complete, more coherent, more beautiful piece of code than symfony 1.0 was. It kicks ass and I believe that it's more powerful than any other PHP framework out there.

On the other hand, I think that symfony 1.1 is more verbose, and therefore more error-prone, than symfony 1.0. It forces you to constantly look at the book for syntax description. The book cannot describe everything - it is just a guide on how to use the framework. It should remain as small as possible to keep readable. The book cannot go into the details of sfCommandArgument::REQUIRED or sfWidgetFormSchema. That means the book will not be enough to use symfony 1.1: You will need to know the full API doc as well, even for the basic uses. Fabien knows the API by heart, so he doesn't need any documentation. But you, my friend, you will sweat a lot to learn all the changes.

The heart of the problem is that, to use symfony 1.1, you don't just need to learn the usage guidelines, you also need to understand how it works inside.

I disagree with the philosophy shift symfony is taking. I think it should stick with the "KISS" motto, and it could do so without sacrificing extensibility. If the price to pay is a little more code in the symfony base, then symfony should pay this price - not the symfony users.

I'm tired of writing a book that looks more and more like an API documentation. I don't feel like explaining the core symfony classes to developers who just wants to use the framework to build an application. I think that The Definitve Guide To Symfony doesn't really make any sense anymore. You want to know how to use the framework? Well, read the API doc and guess. After all, that's what all the other open-source projects do.

Maybe I'm the only one thinking that. But, as I said at the beginning, I'm no developer.

Twitter Said To Drop Rails: So What?

Twitter is said to rewrite parts of their application with something else than RoR to solve their performance/availability issues. Is this good news? Is this a reason for Code Ignitor fans and Django preachers to just celebrate? Should the symfony community see this as a wonderful opportunity?

Not at all. There will be two major consequences, both bad for all frameworks out there. Firstly, IT managers will move away from open-source web application frameworks, because from their point of view, if the most well-known open-source web application framework can't handle the traffic, no other can. Secondly, the frameworks communities look dumber than ever, all fighting with zealot blindness and non-professional arguments. I don't see any enterprise decision maker pick a tool if this tools' support relies on a bunch of guys who just think Their Way Is The Best Way No Doubt About That.

Developers around the world really need to realize that there must not be only one solution. This is called a monopoly. Having many frameworks out there leaves more room for innovation, doesn't concentrate the future of web developement in too few hands. Besides, with millions of web sites out there, the cake is big enough for everybody to get a large piece. If there was only one way to build web apps - say symfony, for instance - then developers would be trapped. And for the cases where symfony would not be the best tool (I can see plenty), there would not be any alternative.

Doubt if the fuel of faith. There are good ideas to grab in every project. You shouldn't rely on the will of a single framework architect for your whole business model. And eventually, success is contagious. So please show your happyness when other frameworks get positive reviews, not the opposite.