My last post on Changes files purported to be both a how-to and how not-to, but I got a bit carried away with the how not-to part. Several comments since that post made excellent points, so here’s the summary.

Kent Fredric says “don’t be lazy and just copy the output from “git log” into your Changelog.” Yes, a thousand times yes! Remember, the Changes file is for end users of your library, and we don’t care about all the little details. Just give us the summary. (Ok, that’s the last how not-to.)

RJBS says “put the latest release at the TOP of the file.” Yes, we’re talking to you, Damian. When I open up the Changes file I don’t want to read about the first release from three years ago.

On #moose IRC, Karen Etheridge also mentioned the importance of linking changes to tickets. The Dancer file I criticized was already doing that, and I meant to highlight that, but got caught up in ranting. All you need is the ticket id and the relevant system, something like “RT #12345″ or “GH #456″ at the end of a particular change.

Karen also suggested mentioning the commit id in the changes file. I’m not so sure about that. I’d rather see some sort of integration between the bug tracker and the source control system. I’ve used such systems at several jobs. I would write “RT 12345″ in my commit message, and that commit message would be appended to the ticket as a comment.

Every software release should come with a list of changes that are present in that release. What goes in this list depends on your audience. Let’s consider a changes file for a module released on CPAN.

For a CPAN module, the audience is other developers. Some of the readers have used this module before, and want to know what’s new. Other readers haven’t used this module, and want to know if the changes in this release will change their minds.

Before we get to the how-to, let’s take a particularly poor example, the Changes file from the Dancer project.

(Note: I’ve got nothing against Dancer, and the devs I’ve talked to in the project all seem to be nice people, but their changes file really is egregiously bad.)

First problem, no release dates. Release dates are really important. They help people see what the development velocity for the project is. There is no excuse for not putting them in your changes file.

(And remember, it’s a release date, not a release timestamp. No one cares that you released the module at 07:23:52 GMT.)

However, there is an even bigger problem, which is that the organization of the changes file is completely and utterly arbitrary. They’ve listed changes by author. This is meaningless to readers.

(I’m all for crediting each contributor, but that should go at the end of each individual change description.)

The changes in a changes file should be grouped in some way that helps readers understand the nature of each change. Ideally, the most important stuff comes first, and least important comes last.

The Dancer changes file has as its second change “turned a tab into the right number of spaces”. Who cares? Does this affect anyone using the code? No.

Overall, the Dancer file has a number of changes listed which no one will care about, like fixing typos in pod. There’s no reason to mention this sort of thing unless the typo in question was a factual error or broken code. If it was just a spelling error, no one really cares.

So there’s no release dates, no useful ordering of changes, and some of the changes are completely unimportant, obscuring the useful information. I give this changes file an F.

For an example of a good changes file, look at Moose.

We separate changes into broad categories that help our readers. The categories are “API CHANGES”, “NEW FEATURES”, “ENHANCEMENTS”, “BUG FIXES”, and occasionally “OTHER”. These categories are always presented in that order.

The most imporant part of the ordering is that API changes (anything which breaks backwards compatibility) are always listed first. The order of the next three is debatable, but that matters less. What’s important is that we break them down into useful categories. If you’re a Moose user who’s been waiting for a specific bug fix, it’s easy to figure out whether the new release fixes that bug.

Prior to 0.93_01, Moose’s changes file wasn’t so great. Changes were listed by the package they affected, which is pretty useless. Moose has a lot of modules, and most of them aren’t exposed to the end user. Knowing that a bug was fixed in Moose::Meta::Role::Application::ToRole doesn’t really help anyone.

If you’re a CPAN author, please think of your intended audience when you write your Changes file. Put the most important stuff first. If you want to break the file down into sections, do that based on something the reader cares about. And don’t forget your release dates!

I’ve just released a new module, CatalystX::Routes, which adds a layer of sugar for declaring Catalyst actions (aka URI mapping, routes, etc).

Route declarations work together with the Catalyst::Action::REST distribution to make it trivial to declare RESTful actions in your controllers.

I’m very excited about this module for several reasons. First, Catalyst’s sub attribute-based action declaration is hideous. I cringe every time I look at it. The syntax is also un-Perlishly picky. For example, these two things are not the same:

sub foo : Chained ('/') : Args (0) { }
sub foo : Chained('/') : Args(0) { }

The difference? Well, the first one is a syntax error. Yes, that’s right, attributes don’t allow spaces before their parameters.

So here’s what some code looks like when converted from “plain old Catalyst controller” to CatalystX::Routes:

sub _set_contact : Chained('/account/_set_account')
                 : PathPart('contact') : CaptureArgs(1) { ... }

becomes …

chain_point _set_contact
    => chained '/account/_set_account'
    => path_part 'contact'
    => capture_args 1
    => sub { ... };

RESTful end points become even cleaner. Now we can get rid of the ugly combination of foo, foo_GET, and foo_POST subs.

sub contact : Chained('_set_contact') : PathPart('')
            : Args(0) : ActionClass('+R2::Action::REST') { }
sub contact_GET : Private { ... }

becomes …

get q{}
    => chained '_set_contact'
    => args 0
    => sub { ... };

We are able to entirely eliminate the do-nothing sub that was needed just to declare a RESTful URI. When you declare a method for an HTTP action, CatalystX::Routes makes sure all the necessary bits are declared behing the scene.

(That get q{} is used to declare a chain end point with the same URI as the mid point it chains from.)

CatalystX::Routes also provides special sugar for providing HTML responses to browsers along with Catalyst::Action::REST::ForBrowsers, so we can write:

get_html q{}
    => chained '_set_contact'
    => args 0
    => sub { ... };

Now when a browser makes a GET request for this URI, we will dispatch to the get_html action.

The real power of CatalystX::Routes goes well beyond making things prettier. Subroutine attributes are parsed by Perl at compile time, and are an entirely separate piece of syntax from other Perl code. In other words, you can’t write this:

BEGIN {
    my $chain = '_set_contact';
    sub contact : Chained($chain) : Args(0) { ... }
}

Well, you can write this, but what happens is that Perl simply parses the Chained attribute as containing the string '$chain'. You cannot use variables in attributes.

With CatalystX::Routes, you declare actions using regular Perl code, which means you can use variables, loops, and so on to make it easy to generate actions.

For example, in one of my controllers, I had several RESTful entities with a very similar set of actions (view a collection, view an individual entity, POST a new entitiy, PUT an update, etc.)

for my $type ( qw( donation note ) ) {
    my $plural = PL_N($type);
       my $entity_chain_point = "_set_$type";
       chain_point $entity_chain_point
           => chained '_set_contact'
           => path_part $type
           => capture_args 1
           => sub {
               my $self = shift;
               my $c    = shift;
               my $id   = shift;
                  my $entity = $class->new( $id_col => $id );
                  $c->stash()->{$type} = $entity;
           };
       put q{}
           => chained $entity_chain_point
           => args 0
           => sub {
               my $self = shift;
               my $c    = shift;
                  my $contact = $c->stash()->{contact};
                  $self->_check_authz( ... );
                  my $entity = $c->stash()->{$type};
                  eval { $entity->update( ... ); };
                  if ( my $e = $@ ) { ... }
                  $c->redirect_and_detach( ... );
           };
}

That’s a mouthful, but there are a few key takeaways. First, I was able to define the mid-point of my chain in a variable named $entity_chain_point, and then use that variable to declare actions:

    chain_point $entity_chain_point ...
        put q{}
            => chained $entity_chain_point ...

I was also able to do the same thing with the path part for the chain mid-point:

    chain_point $entity_chain_point
        => chained '_set_contact'
        => path_part $type ...

And because the subroutines for each action are closures, I’m able to reuse the same subroutine bodies for different entities.

Generating actions programmatically is an incredibly powerful tool for code reuse. I’ve just been using CatalystX::Routes for a day or so, so I’ve really only scratched the surface, but I’m quite excited about the possibilities.

Let me end with a caveat. This is new code, and I’ve only made one release. The API may change without warning, at least for now. And for all I know, this will all turn out to have been a horrible idea, and three months from now I’ll be using subroutine attributes again.

But I doubt it.

I was looking at some $WORK code recently. It had a lot of “stuff” in configuration. This seemed wrong to me, but I wasn’t sure why it bugged me. Thinking about it more, I realized that I had developed a set of unarticulated rules that guide my thinking about configuration.

These guidelines are applicable to more than configuration. A setting can be exposed to non-developers through a configuration file, command line switches, environment variables, etc. However, it seems like configuration files are the most common developer error.

Why does this matter? First, moving behavior into configuration increases the complexity of the code. It’s one more thing for someone working on the code to understand. It requires them to understand your configuration format, and how the code translates that configuration into data and acts on that data. A complex configuration file is essentially another piece of code.

Second, it is a potential support burden. Once you’ve exposed something via an external interface, it becomes more difficult to take back. This parallels the notion that a module’s API should be as small and narrow as possible.

Finally, configuration can make it more difficult for a developer to get things running at all.

Here are my guidelines for when to expose a setting to the user:

Ideally, code should be runnable from a source control checkout.

Catalyst provides a good example. Its built-in server is perfect for development, and even builds in useful debugging options. It’s not appropriate for most production setups, but it works great for development.

If a configuration change requires an accompanying code change, it’s not configuration.

In the $WORK code I looked at, there is a version number defined in the configuration. This code builds an XML file, and the version in the configuration defines the version of the associated XML Schema for that XML file. However, if the schema were to change, the code generating the XML would also have to change. Bumping the version in the configuration file without changing the code would mean that the generated XML has the wrong version. The version should be defined as a constant in the code.

Conversely, if this is data that the developer cannot know the right value of when coding, then it should be externally settable. An obvious example is the ip address a daemon should listen on. Note that this doesn’t necessarily require a configuration file. In the daemon case, a command line switch may be sufficient.

If the configuration item is essentially a data structure only the developer understand, it’s not configuration.

Again, back to the $WORK example. The config file defines a massive mapping from database columns to XML attribute names. While in theory this could be configurable, in practice it is so complicated that no one but a developer working on the code has any use for it. This might as well be a constant data structure in the application code.

Avoid configuration. If you’re an expert, then avoid configuration, mostly.

Ultimately, configuration is just another API that someone has to maintain. It adds complexity and bulk to the code. Avoid it whenever you can.

First, my apologies, as this isn’t strictly related to either activism or programming, but it touches on both. Recently, the programming community has been talking about anti-harassment policies in light of a recent incident.

More recently, my animal rights group, Compassionate Action for Animals, was confronted with the need to create our own policy. Having a public policy in place is important. It makes it clear that the conference/organization takes harassment seriously, and provides a clear direction on how to report alleged harassment.

In the case of my animal rights group, the issue is a little different than with a conference. Conferences take place over a short period of time and they attract a large group of people, many of whom have never met before. In contrast, my animal rights group attracts volunteers and event attendees who we see repeatedly, many of whom stay involved for months or years. Over time, many of the people involved with the group will have repeated contact with each other. Our policy addresses both harassment and general interpersonal disputes.

While thinking about CAA’s policy, I realized that there are several basic scenarios which could lead to a complaint. In each scenario, Person A will be the person who is the alleged harasser, and Person B is the alleged victim. Each scenario has a different ideal outcome.

  • Case 1 – Person A has done something criminal to Person B, such as a physical assault, threat of assault, or stalking. In this case, the best approach is to encourage Person B to contact the police. We should also ask Person A to refrain from contact with the organization until the situation is resolved.
  • Case 2 – Person A is acting out of malice and is knowingly causing Person B to suffer, but their actions are not criminal. Person A is a sadist, and we don’t want them involved with our organization in the future. This is going to be fairly uncommon, but when it happens we need to deal with it decisively. Poisonous people can really hurt an organization.
  • Case 3 – Person A is unknowingly causing suffering to Person B, and their behavior is generally agreed to be unacceptable. This case is going to be more common than cases 1 and 2. The ideal outcome is for Person A to understand why their behavior was problematic, change their behavior, and to stay involved with the organization. However, if Person A refuses to change, they will be asked to leave.
  • Case 4 – Person A is unknowingly causing suffering to Person B, and the acceptability of their behavior is debatable. This case will also be reasonably common. In this case, the ideal outcome is for Person A and Person B to come to an agreement between the two on how they can get along in the future. If they can’t, we still want both of them involved in the organization, but we may need to ensure that we don’t force them to work together.
  • Case 5 – Person A is unknowingly causing suffering to Person B, and their behavior is generally agreed to be acceptable. This case will not be very common. I hesitate to say that Person B is “too sensitive”, since suffering is suffering, but in this case it’s really Person B that is the problem. Any social interaction risks friction, and if Person B cannot tolerate any friction, there is not much the organization can do. In this case, the ideal outcome is to help Person B be a little more tolerant so that they can stay productively involved with the organization. It may also be possible to find tasks that Person B can do alone.

Since three and four are probably the most common cases, our policy should focus on dealing with these in the best way possible. Our ideal outcomes in these cases involve keeping everyone involved in the dispute working with the organization.

However, it’s important to remember that when a complaint is initially made, we really don’t know enough to categorize the complaint, except in the case where the complaint alleges criminal behavior (Case 1). Given that fact, CAA’s current draft policy contains the following goals:

  • CAA wants to be a safe and inviting environment for all participants.
  • We want to respect the concerns of all activists, with attention to fairness and confidentiality.
  • As a social justice organization, we strive to be sensitive to issues such as sexism and racism.
  • CAA wants to maintain neutrality and resolve disputes amicably where possible.

Given these goals, our policy focuses on avoiding the need to appeal for a “judgement”. We need people to be able to work together and resolve disputes if we are going to be effective as an organization. We assume that everyone involved is a reasonable adult, and that they want to get along with each other in pursuit of a common goal. Especially in situations like Cases 3 and 4, we think it’s best for people to simply talk to each other.

We also want to avoid taking sides. If Case 4 is common, it’s reasonable to think that both parties involved may be “right”. We want to make sure that both Persons A and B are given a chance to participate in the process, and our procedure encourages a mediated discussion between the parties before the board will intervene, although we don’t require it, since we can’t really force people to talk to each other.

Compare this to corporate policies, which focus on covering the corporation’s ass against a lawsuit. These policies are full of legalese, and aren’t really oriented towards helping people get along. The policies focus on warnings and judgements, and don’t encourage mediation. This is unfortunate, as it doesn’t do much to help create a more civil society.

Conferences have different needs. They’re short-term, so it’s important to deal with things quickly. Unlike an organization, a conference doesn’t rely on the same people interacting with each other repeatedly over a long period of time. If someone might be causing a problem, it’s important to deal with that quickly in order to keep the conference fun and safe for everyone else. That means that speed becomes more important relative to fairness, though fairness (and the perception of fairness) are still important.

Ultimately, there’s no such thing as a perfect policy. People are imperfect, and there’s too much behavior that falls into a grey area. For people whose behavior is truly unacceptable, we want them to understand how their behavior affects others. When the conflict is greyer, we want both parties to adjust their behavior. The ideal outcome in most cases is for the people involved to keep working together comfortably in the future.