Top Ten Tips for Module Authors

brian_d_foy on 2002-10-24T00:07:42

I have been writing modules for quite a while now, and I continue to learn things. Lately I have been thinking about what I would do if I wanted to re-invent h2xs so that it does the things the way I found that works for me. I have not been silly enough to actually to do, but I have developed a list of tips that I normally do not see the more experienced people passing on to the next generation. Other module writers may have other tips, but these are mine, and they go up to 11 so you know they are good.



  1. Do not reinvent the wheel
    A lot of people say this, so I give it to you for free---it does not count as a tip. Andy Lester tells me we cannot say this too often, so he would not let me get away with leaving this out.

    Check CPAN before you start to write something. With close to 3,000 modules on CPAN, someone has probably already done what you want to do. A lot of the module submissions I see on modules@perl.org are rewrites of other modules the authors did not know about. Save yourself the work of one of us telling you it has already been done.


  2. Read the ExtUtils::MakeMaker manpage
    The ExtUtils::MakeMaker documentation is not all that bad despite the rumors. The WriteMakefile function is virtually infinitely flexible. Almost anything I have needed to do has a setting in WriteMakefile. The documentation has a long list of things you can set in WriteMakefile and with a little experimenting you can do a lot of magic. Scan the documentation and keep it handy. I have it printed and bound in a small binder I keep handy so I can quickly look up a key.


  3. Keep everything in CVS
    CVS saves you from yourself. You can delete important files all you like because `cvs update` brings them back. Almost no one that I have known who seriously decided to use CVS ever went back. Just remember to commit your changes often.


  4. Write the documentation first
    Write the documentation before you write the code. Though it is less of an annoyance to do it sooner than later, it also helps you figure out what to code. As you write the documentation you make decisions about the structure of the code because you define what goes into a function and what comes out, which may change, but you are eliminating many paths that you should not follow. It is hard to see these wrong paths when you are in the middle of code because you emotionally commit to what you do first and it gets harder and harder to move away from that. The documentation, however, is just an explanation so you are still free to code.


  5. Write tests second
    Once you know what you want to code, and what the inputs and outputs to each function are, write tests that check all of those. As you fill in the functions, the tests start to pass, and you always know what you need to do next---make the next test pass by fixing the next function. If you write the tests first, the rest of your development is easy to figure out. Once you have the documentation and the tests you just have to fill in the code with the decisions that you have already made. You spend less time figuring out what to do.


  6. Use Test::More and friends
    Test::More and modules like it make testing easy. You want to write as little code as possible in tests. The more code that you write, the more opportunities you have to make errors. The Test::* modules check potentially complex things with very little visible code and if something does not work, the modules give good diagnostic output.


  7. Use Carp
    If you need to issue a warning, or even worse, die, within your module, make it easy on yourself. If you use warn() or die(), you will get error messages with the filename and line number of the module file which makes you do the work to figure out where in the script you went wrong. You a might call a certain function three times in a script, so which call issues the warnings? You want to know which one does not have the right input or output. Carp's croak() and carp() functions tell you the filename and line number where you called the function which helps you figure out what went wrong.


  8. Use `make disttest`
    Once you are ready to upload your module to CPAN, try a `make disttest`. All of your tests already pass in your development directory, but not everyone has your development directory. Everyone else gets a subset of your files. The `make disttest` creates the distribution archive, then unpacks it and runs the tests in the new distribution directory. You will find out about missing entries in MANIFEST and other hard-to-notice problems in this step.

    I actually wrote a separate script to do the same thing because I did not learn to read the Makemaker documentation. Now I know.


  9. Use MANIFEST.SKIP
    As you add files to your distribution, you also need to add them to MANIFEST. However, you can easily forget to do that when you are thinking about other things. You can use `make manifest` to create MANIFEST automatically, but you probably want to exclude some files. The `make manifest` command automatically excludes certain files (CVS, blib, and so on), but if you add a MANIFEST.SKIP file you need to add those patterns to MANIFEST.SKIP. Once you set up MANIFEST.SKIP, `make manifest` manages MANIFEST for you.


  10. Turn on verbose or debugging output with environment variables
    I often put some sort of debugging flag in my modules. I tend to like $Debug, and some people like $Verbose. At important points in the code I have statements that issue messages.
    print STDERR "The value of foo is [$foo]\n" if $Debug;
    
    For a long time I set this variable by changing the code to whatever value I needed.
    my $Debug = 1;
    
    When I did it that way, I had to change the module every time I wanted to change the value of $Debug. Change is bad. Any time I can make a change I can make an error. Any time I make a change CVS thinks I should commit the change, even though I did not make a real change. If I use an environment variable along with a default value I avoid the hassle.
    my $Debug = $ENV{DEBUG} || 0;
    
    If you want to change the debugging flag in the middle of a run, just use the environment variable directly (instead of supporting another package variable).
    print STDERR "The value of foo is [$foo]\n" if $ENV{DEBUG};
    




  11. Add the distribution pattern to the clean target
    The `make clean` command does not remove old distribution directories (from `make disttest`) or old archives. These can get in the way sometimes. In the WriteMakefile() function in Makefile.PL, Add the distribution pattern to the clean target so when you `make clean`, the old tar balls and directories go away.
    clean => { FILES => 'My-Module-*' }