“Clojure Programming”, the book

I’m very happy to announce that I and Dave Fayram (formerly of Powerset and Microsoft, and now of BankSimple) have recently committed to writing a book on Clojure, tentatively titled “Clojure Programming”, to be published by O’Reilly Media.

This is pretty significant news for me, but likely also for the broader Clojure community.  Having another Clojure book on the shelves is always a Good Thing™, even better if it’s from O’Reilly, the granddaddy of modern technology publishers.  That imprimatur will do nothing but help Clojure gain exposure, and perhaps in circles as yet unaware of the language.

I think the fabulous growth of the community and the (apparent) success of the other books out there have already made it clear that Clojure is here to stay as a serious language, more than ready for use by a broad population of programmers in real, production systems.  Dave and I are just thrilled that we have the opportunity to introduce the language, its facilities, and its general approach to the next wave or two of Clojure programmers.

I’m a better programmer and a better person for having wandered into #clojure in early 2008, and I’m incredibly grateful to have had the opportunity to meet and know the array of wonderful people that have gathered around the language.  I’m hoping this will prove to be an opportunity for me to give back to the Clojure community as it has given to me.

Quickie FAQs

What will be the target audience, table of contents, publication date, &c?

At this point, writing has only recently begun, so there’s much to do and it would be foolish to discuss any specifics.  But, I’m excited, Dave’s excited, and I thought others might be too.

How will this affect Snowtide and Docuharvest?

It won’t.  Development of both PDFTextStream and Docuharvest will continue apace, if not accelerate over the coming months.


That’s all for now.  Wish us luck!

Posted in Books, Clojure, News | 1 Comment

Anthony Simpson (Raynes) will receive his Clojure “scholarship”, thanks to you.

The Clojure community has come through, in a big way.

At 1:26PM, I published the blog post announcing a small, informal fundraiser to help fund a “scholarship” for Anthony Simpson (a.k.a. Raynes), enabling him to attend the first Clojure Conj next month.  Check out the post if you’ve not seen it already; it’s got the backstory.

At 2:38PM, we reached our goal of $1,000 (actually, we overshot it slightly; PayPal doesn’t allow you to cap donations as far as I could tell, so I had to time when I disabled the payment button).  This will enable Anthony and an adult relative of his to travel to the Conj.  I suspect he’ll take full advantage of this opportunity.  And, I’ll bet there’ll be pictures, too. :-)

That’s 72 minutes to raise just over $1,000, donated by 29 different individuals (whose names are listed below), with an average donation of $37.  My original post hit hacker news, so there may be a few non-Clojure programmers mixed in that total that were inspired by the cause – but I recognize most of the names.  In any case, I’m floored.

I knew the Clojure community was phenomenal, but the reception to this effort has been more than even I expected.  I figured we’d make the goal, but 72 minutes? Damn.

Did you feel like you wanted to donate, but the opportunity had already passed?

Please don’t squander that impulse.  This particular effort was worthy, but it’s certainly not unique.  There is no shortage of people who could use a helping hand in one form or another.  Many of them are far away somewhere – and the internet can help in those cases as it has here – but surely there’s somebody right outside your front door, or down the street a little ways that could benefit mightily from that small measure of extra human connection that only you (and maybe a few friends of yours) could provide.

If there’s one thing that this episode has impressed upon me as no prior experience has before, it’s that just a few people (even just one!) can make a difference.  That may sound trite, and it might seem naïve, but I’m quite certain now that it’s true.

Thank you!

To those who donated, thank you so very much:

Craig Andera
Brenton Ashworth
Justin Balthrop
Lance Bradley
Brian Carper
Martin Clausen
Anonymous Donor
Chas Emerick
Mark Engelberg
Antoni Batchelli Estrada
Tom Faulhaber
Christina Gurga
Aria Haghighi
Nathan Hessler Hessler
Richard Hickey
Chris Jones
Zachary Kim
David Liebke
Darwin Lo
Mark McGranaghan
Hans-Christian Meier
William Morgan
Chris Petrilli
Robert Pitts
Luke Renn
Stuart Sierra
Sandeep Srinivasa
Levi Strope
Matt Tessar

Posted in Clojure | 2 Comments

A Clojure “Scholarship”: Let’s send Raynes to the Conj!

If you’re a Clojure (or other FP language) programmer, you might know Anthony Simpson already.  Notable projects of his include sexpbot, irclj, and the guts behind try-clojure.org. He goes by IORayne on twitter, and Raynes in #clojure on freenode irc, where he’s been a positive, helpful presence.

It turns out that he’s 16 years old, is home-schooled in Eldridge, AL (remarkably, population 184!)…and he would like to attend the first Clojure Conj next month in Durham, NC, but he simply doesn’t have the funds to do so.

When I found out about this, I knew there had to be a way to “make it right”.  Now, I don’t know Anthony personally and I can’t “vouch” for him in any traditional sense of the word, but he seems like a fine fellow given my interactions with him on irc: bright, eager to learn, and extraordinarily capable for his age (or, to be fair, for any age). Fundamentally, it seems that helping to get him to the Conj is the right thing to do.  And, at least to some extent, I can relate to his situation – I remember times when I was young(er), strapped for cash, and wishing I could participate in things more typically reserved for older folk in more fashionable locales that were over-employed compared to myself.

So, I talked with the other distinguished invited speakers at the Conj about potentially standing up a fundraiser to help get Anthony to the Conj.  The response was uniformly positive, and Relevance, the conference host and cradle of the Clojure/core team, has graciously offered to take care of his hotel accommodations and admission to the conference (huge thanks to Alan, Justin, David, and anyone else at Relevance that had any hand in making that happen!).

All that’s left to do is raise enough money to pay for transportation (the 10-hour drive between Eldridge and Durham would seem to be unnecessarily epic) and miscellaneous travel expenses (food, cabs, the forgotten toothbrush, whatever).  Either Anthony’s mother or his brother will be traveling and staying with him (definitely necessary for legal and liability reasons), so I figure we’re looking at a $1,000 fundraising target, which will cover:

  • 2x round-trip airline tickets
  • 4 days of misc. travel expenses (the two days of the conference, plus the two traveling days before and afterwards)

It’s a sizable sum, but I think this is a worthy cause, and I suspect the Clojure community will concur.

I’ve set up a PayPal donation button below, along with a progress bar.  Some FYIs:

  • I’ll be manually updating the progress bar, so don’t expect to see it go up immediately after donating!
  • I’ll shut down the donation button once we hit the $1,000 target.
  • I’ll add your name to this page as thanks, unless you notify me otherwise, either (preferably) in the comments associated with your PayPal transaction or by email/twitter/irc.

If you have any questions at all, leave a comment here, or don’t hesitate to contact me if it your question involves confidential details (payment issues, etc).

We did it! Details, numbers, and thanks you’s to all who donated.

Posted in Clojure | 5 Comments

The placebo effect is what makes the software world go ’round

I’ve been of the opinion for some time now that software development, regardless of the methodology followed or the tools used, is not an engineering discipline (unfortunately), but rather is a craft.  I recently laid out that opinion in some detail, which was quickly followed by many people (both in the tubes and in private communications) mostly disagreeing, some suggesting that I just don’t understand engineering particularly well.

In general, I’ll quickly concede that last point, but I keep running into people who ostensibly do understand engineering, and who also reject the notion of software development being a variety of it.  As an example, if I had known of Terence Parr‘s essay “Why writing software is not like engineering” before writing on the same topic with essentially the same premise, I would have simply tweeted a link or something.

A more pointed, and in my opinion, brilliant restatement of this thesis was delivered by Linda Rising in a talk at the Philly ETE 2010 conference in April of this year.  The focus of the talk was far, far broader than the topic of how to classify and characterize software development, and very much worth a listen (audio of the talk is available).  But for now, the key relevant quote comes at 25:15:

One of the things we can learn from psychology is that they do real experiments…whereas in our industry, we do not.  We cannot call ourselves “engineering”; [our work] is not based on science.  In fact, last year at the Agile conference I gave a talk1 that said “I think that mostly what runs this industry is what you’d call the ‘placebo effect’.”

Think about [what would happen] if drug testing were done the way we decide to do anything in software development.  We’d bring in a bunch of pills, and we’d say, “Oh, look at this one, it’s blue! I love blue! And it has a nice shape, oh my goodness, it’s hexagonal! Hexagonal things have always had a special place in my heart.  I think these hexagonal blue ones will be very powerful in solving this disease.  Let’s go with the hexagonals.

I’ll give this hexagonal blue one to all my friends.  I’ll tell them, “This is it, this hexagonal blue one will solve all your problems.  My team tried it, and we really liked it, so your team tried it, you’ll really like it too.”

And that’s how we run projects.  There’s certainly no double-blind controlled experiments.  Is agile any good?  Oh yeah, it is, there’s so much excitement, and  so much buzz, and everybody seems to think so!

Most if not all software developers will recognize the parallels between their profession and that parable.  Why did your team choose C++, or Rails, or F#, or Clojure?  There are often some tangible, technical considerations involved in such choices.  However, as Ms. Rising covers in the talk, human beings are generally not capable of making “rational” choices, but we are stellar when it comes to rationalizing the choices we have made.  So, even our most fundamental decisions – which tools and methods to use – are not made with the rigor that would be demanded of decisions made in an engineering setting.  Of course, this is entirely separate from how we go about actually building things using those tools, which can only be characterized as relative chaos.  How and why we are able to make such abundantly arbitrary choices is something I addressed in my last post on this topic.

And here is where I appeal to a somewhat more respected authority.  Aside from her expertise in the software methodology space, Ms. Rising’s credentials are fairly impeccable, apparently having had some significant involvement in the software development process attached to the design of the Boeing 777 airliner.  So, I’d say she’s in a fairly secure position to be making informed comparisons between engineering, science, and what we do when we build software systems.  Just one more data point, really, but a strong one.


Footnotes

1 A description of this talk is here, but I wasn’t able to find a recording of it.  However, there is an InfoQ interview with Ms. Rising on the topic.

Posted in Craftsmanship, geek | Leave a comment

Xerox’s Inspirational Carlson and Wilson

I recently finished reading Xerox: American Samurai, an out-of-print business case study of sorts that tells the story of Xerox from a mid-1980′s (the book was published in 1986), decidedly American perspective of worrying how domestic industry would compete with the growing influence and capability of Asia, and Japan in particular. It’s a very entertaining read, something of a more business-side Soul of a New Machine: the core of the narrative is the engineering, marketing, manufacturing, and organizational efforts that brought about Xerox’s 10 Series copiers to market starting in 1982 (some of which appear to still be supported and in service!), which were to be Xerox’s response to the accelerating success of its Japanese competitors.  Along the way, the book weaves a story encompassing Xerox’s early days developing and commercializing electrophotography, its fantastic success in the late 1950′s and 1960′s, its “lost decade” in the 1970′s where innovation stagnated and the business began to fray, and finally its then-in-progress rejuvenation into the early 1980′s as Xerox slimmed down and refactored its business and engineering practices to compete effectively.

It’s a great story, but I’m not writing a book review.  Most striking about the book was the glimpse provided of Chester Carlson, the inventor of electrophotography, and Joseph C. Wilson, the co-founder of Xerox (née The Haloid Photographic Company).  As far as I can tell, these men were forces of nature unto themselves, and possessed an array of values and principles that I find inspirational.  Indeed, the book mentions more than once that part of what held Xerox together, especially in the bad years, was the legacy of its progenitors, Wilson and Carlson.

To illustrate, it would be best if I simply quoted from the book; here, from pages 54-56 (bold emphasis mine):

…Wilson never liked it when people referred to Xerox during its spectacular growth years as a Cinderella story.  The company earned its success, he said.  The only magic was the magic of hard work.

As a boy, Wilson grew up in the shadow of Kodak’s largest manufacturing facility in Rochester–Kodak Park.  His dream was to build a company as great as George Eastman’s.  He didn’t want to make a quick killing and then retire with his riches, he wanted his company to have an impact on the world. He wanted to make his company his life’s work, just as Eastman had done.

Chester Carlson and the 914 copier helped Wilson realize his dream. Carlson, the investor of xerography, filed his first patent in 1937, calling his discovery electrophotography.  His first successful image was made in 1938.  Over the next nine years he tried to sell his idea to more than twenty companies, including RCA, Remington Rand, General Electric, Kodak, and IBM.  They all turned him down, wondering why anyone would need a machine to do something you could do with carbon paper.

Although Carlson was often frustrated by the lack of interest in his invention, he never quit.  Sometimes he put his idea and equipment on the shelf for a few months, but soon the enthusiasm would return.  He scraped together a few hundred dollars in 1939, a large sum during the Depression, and had a prototype of an automatic copier built by a model shop in New York.  It didn’t work.  Another model maker got it working, briefly, but soon the war diverted expert machinists to more urgent tasks.  Carlson went back to demonstrating his process with manual plates.  Finally, in 1944, Battelle Memorial Institute in Columbus, Ohio, signed a royalty-sharing agreement with him and began to develop the process.  A short time later, John Dessauer, Haloid’s director of research, showed Joe Wilson a technical article on Carlson’s electrophotography in Radio News. Haloid made the initial contacts with Battelle, and in 1947, it signed an agreement with Battelle and began funding research.  With the help of a professor from Ohio State University, the term “xerography,” Greek for “dry writing,” was coined.

The early manual copying process was excruciatingly slow, almost like developing a photographic print.  An early Haloid brochure describes Thirty-Nine Steps for making good copies on its first commercial copier, the Model A Xerox, which was sometimes called the Ox Box.  The best operators took two to three minutes to make a print, a long way from Carlson’s vision of an automatic machine.  Still, Wilson and Haloid pressed on.  Over the next thirteen years, Wilson committed more money than his company made to developing the process.

Carlson and Wilson both made fortunes on xerography; Carlson earned more than $200 million, Wilson more than $100 million. Their backgrounds and personalities were different, but both of them were reflective men who were concerned with more than money and business.  Carlson was a quiet, shy man from a poor family who struggled to put himself through college and never knew material comfort until late in life when the royalties from xerography finally started to arrive.  During the early years at Haloid, Dessauer once asked him out to lunch.  Carlson declined because he couldn’t afford to reciprocate.  When he made his great breakthrough in xerography he was working days in a patent office, going to law school at night, and doing his experiments on weekends.  He always felt uncomfortable in large groups and avoided public involvement in causes, although he anonymously donated millions of dollars to many of them.

Carlson was never on the regular Xerox payroll, though Wilson made several offers.  Instead, he preferred the independence of working as a consultant.  He died in 1968, at the age of sixty-two, of a heart attack.  A year before his death his wife asked him if he had any unfulfilled desires.  “Just one,” he said. “I would like to die a poor man.”  When he died he had given away more than $150 million. U Thant, secretary-general of the United Nations, sent this tribute to Carlson’s memorial service in honor of his substantial financial contributions: “His concern for the future of the human situation was genuine, and his dedication to the principles of the United Nations was profound.”

Wilson was a graduate of the Harvard Business School.  His father was president of Haloid before him and his grandfather had served as mayor of Rochester.  Unlike Carlson, Wilson was an outgoing person.  His speeches were as likely to contain quotes from Byron and Dostoyevski as they were to contain the latest earnings and revenue numbers.  Even after the company became successful, he would frequently lunch on peanut butter and jelly sandwiches at his desk so he could catch up on his reading.  He welcomes involvement in community affairs, often speaking about the obligation of successful enterprises to contribute to society.   Wilson died in 1971, at the age of sixty-one, of a heart attack, while having lunch with the governor of New York, Nelson Rockefeller.  A frayed, blue index card that he had carried since the early days of his career was found in his wallet.  It summarized his goals: “To be a whole man; to attain serenity through the creation of a family life of uncommon richness; through leadership of a business which brings happiness to its workers, serves well its customers and brings prosperity to its owners; by aiding a society threatened by fratricidal division to gain unity.”

The tenacity, dedication, and grounding principles of these individuals are remarkable, both on spec and compared to the fluff usually offered to entrepreneurs and business owners like myself as examples of success.  Carlson as the inventor and technologist and Wilson as the investor and clueful technical entrepreneur and executive would appear to be far better options.

For those that are interested, it looks like there are at least two other books specifically about Carlson and Wilson, at least in connection with their development of electrophotography and association with Xerox.

Posted in Books, Business, Entrepreneurship, History | Leave a comment

Hosting Maven Repos on Github

Hosting Maven repos has gotten easier and easier over the years.  We’ve run the free version of Nexus for a couple of years now, which owns all the other options feature-wise as far as I can tell, and is a cinch to get set up and maintain.  There’s a raft of other free Maven repository servers, using plain-Jane FTP, and various recipes on the ‘nets to serve up a Hudson instance’s local Maven repository for remote usage.  Finally, Sonatype began offering free Maven repository hosting (via Nexus) for open source projects earlier this year, which comes with automatic syncing/promotion to Maven Central if you meet the attendant requirements.

Despite all these options, I continue to run into people that are intimidated by the notion of running a Maven repo to support their own projects – something that is increasingly necessary in the Clojure community, where all of the build tools at hand (clojure-maven-plugin, Leiningen, Clojuresque [a Gradle plugin], and those brave souls that use Ant + Ivy) require Maven-style artifact repositories.  Some recent discussions on this topic reminded me of a technique I came across a few months ago for hosting a maven repository on Google Code (also available for those that use Kenai).  This approach (ab)uses a Google Code subversion repo as a maven repo (over either webdav or svn protocols). At the time, I thought that it would be nice to do the same with Github, since I’ve long since sworn off svn, but I didn’t pursue it any further then.

So, perhaps people might find hosting Maven artifacts on Github more approachable than running a proper Maven repository.  Thankfully, it’s remarkably easy to get setup; there’s no rocket science here, and the approach is fundamentally the same as using a subversion repository as a Maven repo, but a walkthrough is warranted nonetheless.  I’ll demonstrate the workflow using clutch, the Clojure CouchDB library that I’ve contributed to a fair bit.

1. Set up/identify your Maven repo project on Github

You need to figure out where you’re going to deploy (and then host) your project artifacts.  I’ve created a new repo, and checked it out at the root of my dev directory.

[catapult:~/dev] chas% git clone git@github.com:cemerick/cemerick-mvn-repo.git
Initialized empty Git repository in ~/dev/cemerick-mvn-repo/.git/
warning: You appear to have cloned an empty repository.

Because Maven namespaces artifacts based on their group and artifact IDs, you should probably have only one Github-hosted Maven repository for all of your projects and other miscellaneous artifact storage.  I can’t see any reason to have a repository-per-project.

2. Set up separate snapshots and releases directories.

Snapshots and releases should be kept separate in Maven repositories.  This isn’t a technical necessity, but will generally be expected by your repo’s consumers, especially if they’re familiar with Maven.  (Repository managers such as Nexus actually require that individual repositories’ types be declared upon creation, as either snapshot or release.)

[catapult:~/dev] chas% cd cemerick-mvn-repo/
[catapult:~/dev/cemerick-mvn-repo] chas% mkdir snapshots
[catapult:~/dev/cemerick-mvn-repo] chas% mkdir releases

3. Deploy your project’s artifacts to your Maven repo

A properly-useful pom.xml contains a <distributionManagement> configuration that specifies the repositories to which one’s project artifacts should be deployed.  If you’re only going to use Github-hosted Maven repositories, then we just need to stub this configuration out (doing this will not be necessary in the future1):

<distributionManagement>
	<repository>
		<id>repo</id>
		<url>http://github.com/cemerick/cemerick-mvn-repo/raw/master/releases</url>
	</repository>
	<snapshotRepository>
		<id>snapshot-repo</id>
		<url>http://github.com/cemerick/cemerick-mvn-repo/raw/master/snapshots</url>
	</snapshotRepository>
</distributionManagement>

Usually, URLs provided here would describe a Maven repository server’s API endpoint (e.g. a webdav URL, etc).  That’s obviously not available if Github is going to be hosting the contents of the Maven repos, so I’m just using the root URLs where my git Maven repos will be hosted from; as a side effect, this will cause mvn deploy to fail if I  don’t provide a path to my clone of the Github Maven repo.

Now let’s run the clutch build and deploy our artifacts (which handily implies running all of the project’s tests), providing a path to our repo’s clone directory using the altDeploymentRepository system property (heavily edited console output below)2:

[catapult:~/dev/cemerick-mvn-repo/] chas% cd ../vendor/clutch
[catapult:~/dev/vendor/clutch] chas% mvn -DaltDeploymentRepository=snapshot-repo::default::file:../../cemerick-mvn-repo/snapshots clean deploy
[INFO] Building jar: ~/dev/vendor/clutch/target/clutch-0.2.3-SNAPSHOT.jar
[INFO] Using alternate deployment repository snapshot-repo::default::file:../../cemerick-mvn-repo/snapshots
[INFO] Retrieving previous build number from snapshot-repo
Uploading: file:../../cemerick-mvn-repo/snapshots/com/ashafa/clutch/0.2.3-SNAPSHOT/clutch-0.2.3-SNAPSHOT.jar
729K uploaded  (clutch-0.2.3-SNAPSHOT.jar)
[INFO] BUILD SUCCESSFUL

That looks happy-making.  Let’s take a look:

[catapult:~/dev/vendor/clutch] chas% find ~/dev/cemerick-mvn-repo/snapshots
/Users/chas/dev/cemerick-mvn-repo/snapshots/
/Users/chas/dev/cemerick-mvn-repo/snapshots//com
/Users/chas/dev/cemerick-mvn-repo/snapshots//com/ashafa
/Users/chas/dev/cemerick-mvn-repo/snapshots//com/ashafa/clutch
/Users/chas/dev/cemerick-mvn-repo/snapshots//com/ashafa/clutch/0.2.3-SNAPSHOT
/Users/chas/dev/cemerick-mvn-repo/snapshots//com/ashafa/clutch/0.2.3-SNAPSHOT/clutch-0.2.3-SNAPSHOT.jar
/Users/chas/dev/cemerick-mvn-repo/snapshots//com/ashafa/clutch/0.2.3-SNAPSHOT/clutch-0.2.3-SNAPSHOT.jar.md5
/Users/chas/dev/cemerick-mvn-repo/snapshots//com/ashafa/clutch/0.2.3-SNAPSHOT/clutch-0.2.3-SNAPSHOT.jar.sha1
/Users/chas/dev/cemerick-mvn-repo/snapshots//com/ashafa/clutch/0.2.3-SNAPSHOT/clutch-0.2.3-SNAPSHOT.pom
/Users/chas/dev/cemerick-mvn-repo/snapshots//com/ashafa/clutch/0.2.3-SNAPSHOT/clutch-0.2.3-SNAPSHOT.pom.md5
/Users/chas/dev/cemerick-mvn-repo/snapshots//com/ashafa/clutch/0.2.3-SNAPSHOT/clutch-0.2.3-SNAPSHOT.pom.sha1
/Users/chas/dev/cemerick-mvn-repo/snapshots//com/ashafa/clutch/0.2.3-SNAPSHOT/maven-metadata.xml
/Users/chas/dev/cemerick-mvn-repo/snapshots//com/ashafa/clutch/0.2.3-SNAPSHOT/maven-metadata.xml.md5
/Users/chas/dev/cemerick-mvn-repo/snapshots//com/ashafa/clutch/0.2.3-SNAPSHOT/maven-metadata.xml.sha1
/Users/chas/dev/cemerick-mvn-repo/snapshots//com/ashafa/clutch/maven-metadata.xml
/Users/chas/dev/cemerick-mvn-repo/snapshots//com/ashafa/clutch/maven-metadata.xml.md5
/Users/chas/dev/cemerick-mvn-repo/snapshots//com/ashafa/clutch/maven-metadata.xml.sha1

That there is a Maven repository.  Just to briefly dissect the altDeploymentRepository argument:

snapshot-repo::default::file:../../cemerick-mvn-repo/snapshots

It’s a three-part descriptor of sorts:

  1. snapshot-repo is the ID of the repository we’re defining, and can refer to one of the repositories specified in the <distributionManagement> section of the pom.xml.  This allows one to change a repository’s URL while retaining other <distributionManagement> configuration that might be set.
  2. default is the repository type; unless you’re monkeying with Maven 1-style repositories (hardly anyone is these days), this is required.
  3. file:../../cemerick-mvn-repo/snapshots is the actual repository URL, and has to be relative to the root of your project, or absolute. No ~ here, etc.

4. Push to Github

Remember that your Maven repo is just like any other git repo, so changes need to be committed and pushed up in order to be useful.

[catapult:~/dev/cemerick-mvn-repo] chas% git add *
[catapult:~/dev/cemerick-mvn-repo] chas% git commit -m "clutch 0.2.3-SNAPSHOT"
[master f177c06] clutch 0.2.3-SNAPSHOT
 12 files changed, 164 insertions(+), 2 deletions(-)
 create mode 100644 snapshots/com/ashafa/clutch/0.2.3-SNAPSHOT/clutch-0.2.3-SNAPSHOT.jar
 create mode 100644 snapshots/com/ashafa/clutch/0.2.3-SNAPSHOT/clutch-0.2.3-SNAPSHOT.jar.md5
 create mode 100644 snapshots/com/ashafa/clutch/0.2.3-SNAPSHOT/clutch-0.2.3-SNAPSHOT.jar.sha1
 create mode 100644 snapshots/com/ashafa/clutch/0.2.3-SNAPSHOT/clutch-0.2.3-SNAPSHOT.pom
 create mode 100644 snapshots/com/ashafa/clutch/0.2.3-SNAPSHOT/clutch-0.2.3-SNAPSHOT.pom.md5
 create mode 100644 snapshots/com/ashafa/clutch/0.2.3-SNAPSHOT/clutch-0.2.3-SNAPSHOT.pom.sha1
 create mode 100644 snapshots/com/ashafa/clutch/0.2.3-SNAPSHOT/maven-metadata.xml
 create mode 100644 snapshots/com/ashafa/clutch/0.2.3-SNAPSHOT/maven-metadata.xml.md5
 create mode 100644 snapshots/com/ashafa/clutch/0.2.3-SNAPSHOT/maven-metadata.xml.sha1
[catapult:~/dev/cemerick-mvn-repo] chas% git push origin master
Counting objects: 24, done.
Delta compression using 2 threads.
Compressing objects: 100% (7/7), done.
Writing objects: 100% (19/19), 669.07 KiB, done.
Total 19 (delta 1), reused 0 (delta 0)
To git@github.com:cemerick/cemerick-mvn-repo.git
 f57ccba..f177c06  master -> master

5. Use your new Maven repository

Your repository’s root will be at

http://github.com/<your-github-username>/<your-github-maven-project>/raw/master/

Just append snapshots or releases to that root URL, as appropriate for your project’s dependencies.

You can use your Github-hosted Maven repository in all the same ways as you would use a “normal” Maven repo – configure projects to depend on artifacts from it, proxy and aggregate it with Maven repository servers like Nexus, etc. The most common case of projects depending upon artifacts in the repo only requires a corresponding <repository> entry in their pom.xml, e.g.:

<repositories>
    <repository>
        <id>cemerick-snapshots</id>
        <url>http://github.com/cemerick/cemerick-mvn-repo/raw/master/snapshots</url>
    </repository>
</repositories>

Advantages

  1. Administrative simplicity: there are no servers to maintain, no additional accounts to obtain (i.e. compared to using Sonatype’s OSS Nexus hosting service), and the workflow is very familiar (at least to those that use git).
  2. Configuration simplicity: compared to (ab)using Google Code, Kenai, or any other subversion host as a Maven repository, the project configuration described above is far simpler.  Subversion options require adding a build extension (for either wagon-svn or wagon-webdav) and specifying svn credentials in one’s ~/.m2/settings.xml.
  3. Tighter alignment with the “real world”: ideally, every artifact would be in Maven Central, and every project would use Hudson and deploy SNAPSHOT artifacts to a Maven repo.  In reality, if you want to depend upon the bleeding edge of most projects – which aren’t regularly built in a continuous integration environment and which don’t regularly throw off artifacts from HEAD – having an artifact repository that is (roughly) co-located with the source repository that you control is very handy.  This is true even if you have your own hosted Maven repo, such as Nexus; especially for SNAPSHOTs of “vendor” artifacts, it’s often easier to simply deploy to a local clone of your git-hosted Maven repo and push that than it is to recall the URL for your Maven server’s third-party snapshots repository, or constantly be adding/modifying a <distributionManagement> element to the projects’ pom.xml.

Caveats / Cons

  1. This practice may be considered harmful by some.  Quoting here from comments on another description of how to host Maven repositories via svn:

    This basically introduces microrepository. Users of these projects require either duplicate entries in their pom: one for the dependency and one for the repository, or put great burden on the maintainer of the local repository to add each microrepository by hand….So please instead of this solution have your Maven build post artifacts to a real repository like Java’s, Maven’s or Sontatype’s.
    – tbee

    Please do NOT use this approach. Sonatype provide free hosting of a Maven Repo for open source projects and BONUS! you get syncing to Maven Central too for free!!!
    Stephen Connolly

    tbee’s point is that, the more “microrepositories” there are, the more work there is for those that maintain their own “proper” Maven repository servers, all of which provide a proxying feature that allows users of such servers (almost always deployed in a corporate environment as a hedge against network issues, artifact rot, and security/provenance issues, among other things) to specify only one source repository in their projects’ pom.xml configurations, even if artifacts are actually originating from dozens or hundreds of upstream repositories.  I don’t really buy that argument, insofar as the cat is definitely out of the bag vis á vis a proliferation of Maven repositories. Our Nexus install proxies around 15 Maven repositories in addition to Maven Central, and I suspect that that’s a very, very low compared to the typical Nexus site. I’ll bet there are hundreds – perhaps thousands – of moderately-active Maven repositories in the world already.

    I agree with Stephen that if you are willing to get set up with deployment rights to Sonatype’s OSS Maven repo, you should do so.  Not everyone is though – and I’d rather see people using dependency management than not, and sharing SNAPSHOT builds rather than not. In any case, if you use this method, know that you’ve strayed from the Maven pack to some extent.

  2. The notion of having to commit the results of a mvn deploy invocation is very foreign.  This is particularly odd for release artifacts, the deployment of which are ostensibly transactional (yes, the git commit / git push workflow is atomic as far as other users of the git/Maven repository are concerned, but I’m referring to the deployer’s perspective here).  The subversion-based Maven hosting arrangements don’t suffer from this workflow oddity, which is nice.  I suppose one could add a post-commit hook to immediately push results, but that’s just illustrating the law of conservation of strangeness, sloughing off unusual semantics from Maven-land to the Realm of git.  You could fall back to using Github’s svn write support, but then you’re back in subversion-land, with the configuration complexity I noted earlier.
  3. Without a proper Maven repository server receiving deployed artifacts, there will never be any indexes offered by these git-hosted Maven repos.  The same goes for subversion-hosted Maven repos as well. Such indexes are a bit of a niche feature, but are well-liked by those using Maven-capable tooling (such as the Eclipse and NetBeans IDEs).  It would be possible to generate and update those indexes in conjunction with the deployment process, but that would likely require a new Maven plugin – a configuration complication, and perhaps enough additional work to make deploying to Sonatype’s OSS repo or hosting one’s own Maven repository worthwhile.

Footnotes

  1. The fact that Maven currently requires a stubbed-out <distributionManagement> configuration is a known bug, slated to be fixed for Maven 2.5. Specifying the deployment repository location via the altDeploymentRepository will then be sufficient.
  2. An alternative to using the altDeploymentRepository option would be to make the git Maven repository a submodule of the project repository.  This would require that the git Maven repo be the canonical Maven repo for the project, and would imply that all the usual git submodule gymnastics be used when deploying to the git Maven repo.  I’ve not tried this workflow myself, but might be worth experimenting with.
Posted in Clojure, Maven, Random Software Geekery, geek | 3 Comments

Case-insensitive filesystems vs. AOT-compiled Clojure

I recently ran into a problem related to ahead of time-compiling (a.k.a. AOT) some Clojure code that took me a little while to figure out. Here, I’d like to leave behind some breadcrumbs for anyone else that happens to run into the same problem.

Consider a namespace at ./foo/myns.clj:

(ns foo.myns)
(defn hi [])
(defn HI [])

Let the fun begin. AOT-compile the namespace into the cwd:

[catapult:/tmp] chas% java -Dclojure.compile.path=. -cp clojure-1.2.0-RC3.jar:. clojure.lang.Compile foo.myns
Compiling foo.myns to .
[catapult:/tmp] chas% find foo
foo
foo/myns$hi.class
foo/myns$loading__4403__auto__.class
foo/myns.clj
foo/myns__init.class

Ut-oh.  There are two defined fns, hi and HI, but there’s only one classfile, ostensibly for hi (note that each Clojure function AOT-compiles down to its own classfile).

[catapult:/tmp] chas% java -cp clojure-1.2.0-RC3.jar:foo clojure.main
Clojure 1.2.0-RC3
user=> (require 'foo.myns)
java.lang.NoClassDefFoundError: foo/myns$hi (wrong name: foo/myns$HI) (NO_SOURCE_FILE:0)
user=> foo.myns$hi
java.lang.NoClassDefFoundError: foo/myns$hi (wrong name: foo/myns$HI) (NO_SOURCE_FILE:0)

What’s going on here?

It took me a while (probably a few hours) of false starts before things clicked:

  • My disk is formatted as Mac OS Extended (aka HFS+), but not the case-sensitive variety (the fs is still case sensitive insofar as it retains case information in filenames [just for display purposes as far as I can tell], but that’s as far as it goes).
  • When the compiler writes out the classnames for each fn in the namespace, the second one it writes out goes to the same location as the first, but has a different name as far as the classloader is concerned.
  • The (require 'foo.myns) invocation uses the classloader to find a class named foo.myns, which goes on to load the classes associated with each of the functions defined in that namespace (or, .clj files are found and loaded, if they’re newer than any classfiles or if there are no matching classfiles).
  • The classloader finds a classfile for the foo.myns/hi function, but the class’ internally-defined name (HI) doesn’t match the requested name (hi), so an exception is thrown.

As a sanity check, I mounted a ramdisk, formatted it with case-sensitive HFS+, and voilà, I could AOT-compile myns.clj and require it without a problem.  Filesystem case-sensitivity isn’t usually something one has to worry about with most languages: if you can’t name identical-except-for-case source files on disk, then you are saved from being in a position of potentially compiling to identical-except-for-case class or object files.  However, because of how Clojure maps code to classfiles (remember, one classfile per function, with source files [usually!] designating namespaces), it’s not until one attempts to load a Clojure namespace from AOT-compiled classfiles does one run up against any trouble.

Solutions

The easiest solution is to simply not AOT-compile your Clojure code.  Avoiding AOT carries no runtime performance penalty (though initialization of each namespace will be slower), and source distributables will always be smaller.  Of course, there are many nontechnical and some technical reasons why AOT-compilation is a necessity in various circumstances, so source distributions certainly won’t be right for everyone.

At some point while reading this, you’ve probably said under your breath, “Well, you shouldn’t have function names that differ only in case anyway!”  I agree wholeheartedly, and it must be said that the above situation is highly exceptional.

However, sometimes one isn’t necessarily choosing function names – this is often the case when when generating code from some dataset.  In my case, I was generating functions for handling PDF tokens, many of which differ only in case (e.g. Tj and TJ are one example).  The general solution when you’re in a circumstance like this is that your macro (or other code-generating facility) needs to take care to mangle the function names being emitted so that they are guaranteed to be unique.

A trickier question is whether the Clojure compiler should be doing something to prevent this scenario due to accidental function name collision.  Off the top of my head, I can imagine simply checking for existing/matching classfiles on disk that nonetheless have non-equal filenames would be sufficient cause to raise an exception when AOT-compiling.  That change, or something similar, may yet come to pass; until then, hopefully the above will be helpful.

Posted in Clojure | Leave a comment

Enhancing Clojure’s case to evaluate dispatch values

Update: Christophe Grand has pointed out a flaw in the case+ implementation below that affects usage of Java enums and classes in code that is AOT-compiled.  My bad.  I’m working on an update to case+ that will address the problem for enums – classes will likely revert to being unsupported.

For those that aren’t familiar, Clojure’s case macro is an über-switch statement that allows one to dispatch on any compile-time literal in constant time. This includes essentially all scalars (strings, keywords, symbols, numbers) and “composites thereof” (as the docs put it): literal sets, maps, and vectors. The latter feature pushes case a fair ways into pattern-matching territory.

Brief factoid interlude

Clojure’s defrecord would not exist as we know it without case, which backs the implementation of defrecord‘s keyword lookup mechanism.  Without that, records would be without the congruence with regular maps that they currently enjoy: each slot access would either need to run through get (e.g. (get some-record :slot-name)) or direct field access would be required (which would rule out function composition with keyword “accessors”, the use of the -> and ->> families of threading macros, etc).

The only downside of case is that it doesn’t evaluate the dispatch values that are provided. Only compile-time constants are matched, so if one has values that are defined in a var, accessible as (almost always static) Java class fields, or Java enums, you can’t use them as case dispatch values:

user=> (case javax.swing.JFrame/NORMAL
         javax.swing.JFrame/NORMAL "normal!")
java.lang.IllegalArgumentException: No matching clause: 0 (repl:78)
user=> (case 'javax.swing.JFrame/NORMAL
         javax.swing.JFrame/NORMAL "normal!")
"normal!"

As you can see, the dispatch value referring to the NORMAL static int field is being matched as a symbol, rather than the field it names. The same thing happens when attempting to refer to Java enums, values held by vars, classes, and so on. This is a problem insofar as I really don’t want to fall back to the sequential dispatch of cond or condp, but I need to use dispatch values that are more than just compile-time literals. My current solution is the poorly named case+ macro:

(defmacro case+
  "Same as case, but evaluates dispatch values, needed for referring to
   class and def'ed constants as well as java.util.Enum instances."
  [value & clauses]
  (let [clauses (partition 2 2 nil clauses)
        default (when (-> clauses last count (== 1))
                  (last clauses))
        clauses (if default (drop-last clauses) clauses)
        eval-dispatch (fn [d]
                        (if (list? d)
                          (map eval d)
                          (eval d)))]
    `(case ~value
       ~@(concat (->> clauses
                   (map #(-> % first eval-dispatch (list (second %))))
                   (mapcat identity))
           default))))

With this available, the interop love flows like butter, and all of the dispatch literals already supported by case are unaffected because they all evaluate to themselves (FYI, Direction is a pre-existing enum class of mine):

user=> (case+ javax.swing.JFrame/NORMAL
         javax.swing.JFrame/NORMAL "normal!")
"normal!"
user=> (case Direction/East Direction/East "east!")
user=> java.lang.IllegalArgumentException: No matching clause: East (repl-1:88)
user=> (case+ Direction/East
         Direction/East "east!")
"east!"
user=> (case String
         String "it's a string class")
java.lang.IllegalArgumentException: No matching clause: class java.lang.String (repl-1:90)
user=> (case+ String
         String "it's a string class")
"it's a string class"
user=> (def some-constant 42)
#'user/some-constant
user=> (case 42
         some-constant "Towels for everyone!")
java.lang.IllegalArgumentException: No matching clause: 42 (repl-1:93)
user=> (case+ 42
         some-constant "Towels for everyone!")
"Towels for everyone!"

The big caveat is that the values used for dispatch in case+ are still resolved at compile-time – it is a macro, after all.  So, you can’t put case+ in a let form and dispatch on a let-bound value (attempting to do this will lead to an error, as locals cannot be eval‘ed).  Dispatch only on values available at compile-time, such as classes and class fields (which include enum instances).

I’ve used this extensively with good results (case+ accounts for perhaps a 50% of my total case usage), but I am just slightly concerned about my doing an end-around the key behaviour of case in not evaluating dispatch values. I didn’t happen to be paying attention in irc when the case design discussions were ongoing, and I don’t yet fully understand the motivation of the restriction. So, I offer this up somewhat tentatively for now – your mileage may vary. Do pipe up if you find a situation were case+ falls down.

Posted in Clojure | 2 Comments

Specifying default slot values for defrecord classes in Clojure

Clojure’s datatypes are mana in a variety of ways.  In particular, for application-level programming, defrecord is the near-ideal construct — baked-in protocol support, Java interface interop, direct field storage of slots, fast keyword “accessors”, fast-as-Java method implementations…the list of goodness goes on and on.

Of course, there’s always room for improvement.  One big pain point for me was that I have a couple of records that, due to the domain, have a lot of slots, each of which needs to have a specific default value.  defrecord‘s generated constructor doesn’t provide for any kind of default value for slots.  The naive solution would be to write a factory function that aligned its arguments with some defined set of defaults, but that sounded painful from the start: slot definitions are then functionally spread over multiple forms and the handling of arguments into that factory function would either be a maintenance or performance nightmare.

It’s good to avoid macros as much as you can, but cases like this is where they shine:

(defmacro defrecord+defaults
  "Defines a new record, along with a new-RecordName factory function that
   returns an instance of the record initialized with the default values
   provided as part of the record's slot declarations.  e.g.
   (defrecord+ Foo [a 5 b \"hi\"])
   (new-Foo)
   => #user.Foo{:a 5, :b \"hi\"}"
  [name slots & etc]
  (let [fields (->> slots (partition 2) (map first) vec)
        defaults (->> slots (partition 2) (map second))]
    `(do
      (defrecord ~name
         ~fields
         ~@etc)
       (defn ~(symbol (str "new-" name))
         ~(str "A factory function returning a new instance of " name
            " initialized with the defaults specified in the corresponding defrecord+ form.")
         []
         (~(symbol (str name \.)) ~@defaults))
       ~name)))

This macro is breaking my general rules of thumb that no def form should define more than one entity (here, a class and a factory function), and that def forms shouldn’t create vars with generated names.  Unfortunately, I don’t think there’s any other better approach to be had given what I’m aiming to do.

There’s an example in the docstring for defrecord+defaults, but let’s take a look at some possible usage in a REPL interaction:

user=> (defrecord+defaults SomeClass
         [size 0
          root nil
          color java.awt.Color/black])
user.SomeClass
user=> (doc new-SomeClass)
-------------------------
user/new-SomeClass
([])
  A factory function returning a new instance of SomeClass initialized with the defaults specified in the corresponding defrecord+ form.
nil
user=> (new-SomeClass)
{:size 0,
 :root nil,
 :color
 #<Color java.awt.Color[r=0,g=0,b=0]>}
user=> (assoc (new-SomeClass)
         :size 1
         :root "foo")
{:size 1,
 :root "foo",
 :color
 #<Color java.awt.Color[r=0,g=0,b=0]>}

Yup, macros to the rescue (yet again!). What’s particularly handy about this is that the defined defaults can be any Clojure expression, which is evaluated each time the factory function is invoked. A neat future enhancement would be for the factory function to have a keyword-argument override that takes slot names and values that supersede the corresponding defaults provided in the defrecord specification.

I wouldn’t be surprised if something like this ends up in Clojure itself eventually, as a specialization of a future defrecord factory function facility.

Posted in Clojure | 9 Comments

Programming and software development, medium-rare

In a past life as a “software engineer” on contract, a favorite analogy of coworkers was to compare software development to construction (perhaps influenced by the early 2000′s housing boom?). Projects were like houses, plans were needed to properly architect the end result, and schedules were laid down to ensure that the “foundations” were done before we started “framing” the building and “furnishing” the details. Conceptualizing software development in such a way is common, and there’s a long history of people involved in what has casually been called “software engineering” thinking that what they do is, or should be, related to “old-world” engineering.

This is largely both nonsense and decidedly unnerving.

I’m not like my grandfathers

Both of my grandfathers were involved in engineering; knowing something of what they did makes me even more sure that what I do is not related to engineering.

One was an electrician, more a tradesman than an engineer, who worked in the construction of large commercial buildings in downtown Hartford and Denver. Each day, he would look at the blueprints painstakingly drafted by the project’s architects and engineers, and go about making those plans a reality – installing high-voltage switching equipment, stringing miles of Romex (or, whatever they used back then), and doing all of it in hazardous conditions.

My other grandfather was, as far as I remember, something of a process engineer at a subsidiary of Olin, helping to design the manufacturing processes that would heat, pound, roll, cut, stamp, and test thousands of varieties of copper and stainless steel foils, strips, and other formulations for later inclusion in all sorts of products, both industrial- and consumer-related.

These men’s careers were very different, but they were involved in what are clearly engineering tasks.

An art’s constraints are what define the art

There’s a lot that separates my discipline from my grandfathers’, but I think the most significant is that, as someone who builds software, I have far more discretion in how I achieve my end results than they had in their work. The degree to which this is the case cannot be overstated, but I’m at a loss for words as to how to concisely characterize it. Materials in the real world behave in ways that are, in the modern age anyway, understood: electricity and copper and steel and wood have known physical characteristics and respond in known ways to all of the forces that might be applied to them.1

In contrast, the world of software has so many degrees of freedom in so many vectors, the possibilities are functionally limitless. This is both a blessing and a curse, as it means that the programmer is something of a god within her domain, free to redefine the its fundamental laws at will. Given this context, it’s no wonder that software projects fail at an astounding rate: we simply have nothing akin to the known natural constraints that are conveniently provided for our real-world engineer friends, so we have no option but to discover those constraints as we go along.

The software community’s response to this has been to erect artificial constraints in an effort to make it possible to get things done without simply going insane: machine memory models with defined semantics, managed allocation, garbage collection, object models, concurrency models, type systems, static analysis, frameworks, best practices, software development methodologies for all stripes and inclinations. This is natural, and good, and the only way we could possibly make sense of this thing called software given how young it is.

Yet, even after all this edifice, ask 100 software developers how to build a website, and you will get at least 500 answers – and the people that respond with a hesitant “It depends” are likely the most clueful of the group. If my grandfather had responded “It depends” to a question about how to produce a 1-ton spool of 2mm-thick copper strip, he’d have been fired on the spot.2

Confessions and snotty ego trips

Interlude: If Top Chefs were programmers

Susur’s work is remarkably intricate and exhibits a delicate precision unmatched by his peers. He prefers the crystalline perfection of a perfectly-balanced type system, and so chooses Haskell for most of his work.

Living up to his “Obi-Wan” moniker, Jonathan makes the most familiar t hings amazing, and people are never sure how. His secret is that he usually works in some lisp or scheme, which he then uses to generate whatever comforting form his customers prefer, often Java or C#.

Marcus likes to surprise people with the most esoteric things possible: his pièce de résistance is written using his own prolog variant, implemented in Factor. People love it either for the ballsiness of it all, or because his stuff works so well they don’t notice.

Rick is a madman, insisting on using C++ for everything, even web applications.

Okay, so software development isn’t engineering. It’s probably safe to say it’s a craft, though (in contrast to pure art like painting or sculpting, but that’s a different blog post). In particular, its similarities to cooking are legion, something that I noticed while watching one of the few bits of television I bother with (and a guilty pleasure), Top Chef (though I redeem myself oh-so-slightly by preferring the Masters incarnation by a mile). *blush*

Watching this show with an eye towards the methods and attitudes of the chefs is like watching a group of programmers struggle to build quality software. Functionally no constraints in process, methodology, and materials? Check. Infinitely many ways to get the job done depending on the skill of the craftsman, the tastes of the customers, and the specifics of the materials? Check. Identiying the best craftsmen is difficult, and most of those at the top of their field have a murky combination of ill-defined qualities? Check. A wide disparity between the effectiveness of individuals in the field? Check. At the edges, people experiment with hacks that sometimes come to be part of everyone’s repertoire? Check. Technical capability is not a necessary requirement for success, due to fluctuating trends (less charitably called “fashion”) and a variety of potentially-compensating personal characteristics? Check. Less mature (and often less capable) members of the community are given to snotty ego trips, bad attitudes, and frequent tantrums? Check.

Given all of the similarities, I think the fact that it’s difficult to assess the quality of individuals in either field is the most striking (and perhaps a key indicator that a field has not (or intrinsically cannot) internalized a certain minimum degree of rigor in its methods). It’s telling that TopCoder predated Top Chef by a decade. Great hackers and those guys that can hit the high notes are few and far between, mixed in with scads of cooks that come to work stoned and “programmers” that put HTML on their resumés that still manage to get hired, somewhere. These are fields that are far from science, far from engineering, and well within the lush, rolling hills of craft.

Enough already. So what?

First, words matter and it’s important to call a spade a spade. Thoughtlessly (or disingenuously) call software development “engineering”, and people walk off with all sorts of notions that are inappropriate. This is particularly damning with customers and other nontechnical “civilians”.

Second, craft, as revered a concept as it is, is not desirable when businesses, livelihoods, and actual lives are at stake. I’ve never worked on aeronautics software or the like, but despite its codified standards, its rigorous testing protocols, and access to millions and billions of dollars in resources, we’ve crashed satellites into planets because of something as triflingly simple as conversion between metric and standard measures. Similar examples are everywhere. We need software development to be built with an engineering discipline, because everything we do depends upon it.

Of course, I have no tidy answers for that challenge. I think that if we can pull ourselves out of this primordial ooze of twiddling bits and get to a point where we describe how to do things relevant to the domains we’re working in, then I think there’s a chance for the species to survive. Ideally, domain experts should be doing the bulk of the “programming” work, given that communication between such experts and programmers is probably the source of at least half, if not the vast majority of software design flaws. This is an old chestnut, dating back to the 4GL programming languages (now 5GL?), declarative programming, expert systems, and so on.

Programmers’ work will be done when we’ve put ourselves out of a job

Roughly, I think the point is to solve for X:

blueprint:building :: X:code 3

One strain of real-life examples – such as Pipes, DabbleDB, and WuFoo – allow people to collect and process data without touching a line of code. The best specimens of these kinds of systems are often labeled with the “tool” slur by programmers, but the fact is such things allow for greater aggregate productivity and less possibility for error than any similar purpose-built application. Meta-software that can deliver the same for any given domain is the future, and the result will be that programmers, as they are known today, should cease to exist in commercial settings.

The craft of programming will survive though, and continue to be the source of innovation. After all, there’s no shortage of demand for great chefs, even with all these McDonald’s and Olive Gardens dotting the landscape.

1 There are obviously still unknowns in the physical world, but those are irrelevant insofar as we’re contrasting engineering and software development within the scope of commercial projects. Research matters are an entirely separate issue in both camps.
2 Another good question to ask: Can you estimate time and materials for projects…and be correct within a 100% margin of error? If so, congratulations, yours might be an engineering discipline.
3 From an informal, but very high quality discussion on this topic in #clojure IRC: http://clojure-log.n01se.net/date/2010-06-04.html#11:33e

Posted in Craftsmanship, geek | Tagged | 6 Comments