Software · 5 min read

Building FeatureCorr: lessons from a small R package

A script became a published package. The statistics were the easy part.

FeatureCorr started as a script. I needed to look at feature correlations across sequencing and microarray data, with the data transformation step handled properly rather than bolted on afterwards, and nothing I reached for did quite that.

It ended up as a published R package. That transition, from a file that works on my machine to something a stranger can install, turned out to be most of the work, and almost none of it was the statistics.

Scope is the whole design

The version that shipped does less than the version I first imagined. That was the right call. A package that computes correlations with sensible transformations, and does that reliably, is more useful than one that also attempts clustering, plotting, and enrichment, each slightly worse than the dedicated tool.

The test I now apply: if a feature would make me write "see also" in the documentation pointing at a better package, it does not belong in mine.

Defaults are your real interface

Most people will never change a default. Whatever the function does with no arguments is, in practice, what your package does.

That reframing changed several decisions. Which transformation is applied when the user says nothing? What happens with missing values: silently dropped, or an error? I moved toward erroring loudly on ambiguity, because a wrong answer that runs is worse than a stop that makes someone think.

Documentation is not the last step

I wrote the README after the code, and that was backwards. Writing the usage example first exposes the awkward parts of the interface while they are still cheap to change. Twice I renamed an argument purely because the example sentence read badly.

If you cannot write a clean three-line example, the API is not finished.

What I would do differently

  • Write tests from real data earlier. Synthetic test cases pass for the wrong reasons. A small slice of a genuine dataset, checked into the repo, catches the failures that actually happen.
  • Pin the environment from day one. Dependency drift is the most common reason a two-year-old analysis stops running, and it is entirely preventable.
  • Decide the citation story up front. If you want the package to be cited, the DOI, the recommended citation, and the version history need to exist before anyone uses it.

Was it worth it?

Yes, but not primarily for the citation. Packaging forced me to state my assumptions explicitly, in code, where they could be checked. Several were wrong. I would rather have found that out while writing documentation than in review.

All posts