Test Yourself

By the time I got to Penn for my first year of college, I thought I was already a pretty good programmer. Completely self-taught, I had written two major systems in Turbo Pascal … one of them was a complete inventory system for a small factory, while the other scheduled all the production at one of Israel’s largest bakeries.

It took me until the midterm exams to realize I wasn’t as smart as I thought. I completely screwed up some questions, because I still didn’t get pointers and I still didn’t get recursion.

Never one to hold a grudge, I share those midterm questions with you… see if you can do better than I did freshman year.

1a. (MIT-Scheme) Using the function

(define (accumulate combiner null-value l) 
   (if (null? l)
       null-value
       (combiner (car l)
                 (accumulate combiner
                             null-value
                             (cdr l)))))

Implement sum-of-squares, which calculates the sum of squares of a list, for example

(sum-of-squares '(1 2 3 4 5))

should evaluate to 55.

(For the answer, select the text in the box:)

(define (sum-of-squares l)
   (accumulate (lambda (x y) (+ (* x x) y))
               0
               l))

1b. (JavaScript) OK, maybe Scheme is not your thing. This question is equivalent to 1a, in JavaScript.

Using the function

function accumulate(combiner, nullValue, l)
{
	if (l.length == 0)
		return nullValue;
	var first = l.shift();
	return combiner(first, accumulate(combiner, nullValue, l));
}

Implement sumOfSquares, which calculates the sum of squares of a list, for example

sumOfSquares([1,2,3,4,5])

should evaluate to 55.

(For the answer, select the text in the box:)

function sumOfSquares(lst)
{
	return accumulate(function(x,y){return x*x+y}, 0, lst);
}

2. (ANSI C) Write a C program of the following form:

#include <stdio.h>
int main(int argc, char **argv)
{
   ???
}

such that, after compiling it, it can be executed as

% ./a.out could harold eat eight salami elephants

and it will print the first letter of every argument (in this case, “cheese”).

(For the answer, select the text in the box:)

#include <stdio.h>
int main(int argc, char** argv)
{
  int i;
  for (i=1; i<argc; i++)
  {
    putchar(argv[i][0]);
  }
  putchar('\n');
  return 0;
}

3. (ANSI C) What is the output of the following C program?

#include <stdio.h>

char *fn(int i, char *s)
{
  while (i)
  {
    s++;
    i--;
  }

  return s;
}

int main(int argc, char** argv)
{
  int a = 2;
  static char c[] = "test";

  printf("%s\n", fn(a,c));

  return 0;
}

(For the answer, select the text in the box:)

st (followed by a newline)

For more programming challenges, check out TopCoder.

The Perils of JavaSchools

Lazy kids.

Whatever happened to hard work?

A sure sign of my descent into senility is bitchin’ and moanin’ about “kids these days,” and how they won’t or can’t do anything hard any more.

“You were lucky. We lived for three months in a brown paper bag in a septic tank. We had to get up at six in the morning, clean the bag, eat a crust of stale bread, go to work down the mill, fourteen hours a day, week-in week-out, and when we got home our Dad would thrash us to sleep with his belt.” — Monty Python’s Flying Circus, Four Yorkshiremen

When I was a kid, I learned to program on punched cards. If you made a mistake, you didn’t have any of these modern features like a backspace key to correct it. You threw away the card and started over.

When I started interviewing programmers in 1991, I would generally let them use any language they wanted to solve the coding problems I gave them. 99% of the time, they chose C.

Nowadays, they tend to choose Java.

Now, don’t get me wrong: there’s nothing wrong with Java as an implementation language.

Wait a minute, I want to modify that statement. I’m not claiming, in this particular article, that there’s anything wrong with Java as an implementation language. There are lots of things wrong with it but those will have to wait for a different article.

Instead what I’d like to claim is that Java is not, generally, a hard enough programming language that it can be used to discriminate between great programmers and mediocre programmers. It may be a fine language to work in, but that’s not today’s topic. I would even go so far as to say that the fact that Java is not hard enough is a feature, not a bug, but it does have this one problem.

If I may be so brash, it has been my humble experience that there are two things traditionally taught in universities as a part of a computer science curriculum which many people just never really fully comprehend: pointers and recursion.

You used to start out in college with a course in data structures, with linked lists and hash tables and whatnot, with extensive use of pointers. Those courses were often used as weedout courses: they were so hard that anyone that couldn’t handle the mental challenge of a CS degree would give up, which was a good thing, because if you thought pointers are hard, wait until you try to prove things about fixed point theory.

All the kids who did great in high school writing pong games in BASIC for their Apple II would get to college, take CompSci 101, a data structures course, and when they hit the pointers business their brains would just totally explode, and the next thing you knew, they were majoring in Political Science because law school seemed like a better idea. I’ve seen all kinds of figures for drop-out rates in CS and they’re usually between 40% and 70%. The universities tend to see this as a waste; I think it’s just a necessary culling of the people who aren’t going to be happy or successful in programming careers.

The other hard course for many young CS students was the course where you learned functional programming, including recursive programming. MIT set the bar very high for these courses, creating a required course (6.001) and a textbook (Abelson & Sussman’s Structure and Interpretation of Computer Programs) which were used at dozens or even hundreds of top CS schools as the de facto introduction to computer science. (You can, and should, watch an older version of the lectures online.)

The difficulty of these courses is astonishing. In the first lecture you’ve learned pretty much all of Scheme, and you’re already being introduced to a fixed-point function that takes another function as its input. When I struggled through such a course, CSE121 at Penn, I watched as many if not most of the students just didn’t make it. The material was too hard. I wrote a long sob email to the professor saying It Just Wasn’t Fair. Somebody at Penn must have listened to me (or one of the other complainers), because that course is now taught in Java.

I wish they hadn’t listened.

Think you have what it takes? Test Yourself Here!

Therein lies the debate. Years of whinging by lazy CS undergrads like me, combined with complaints from industry about how few CS majors are graduating from American universities, have taken a toll, and in the last decade a large number of otherwise perfectly good schools have gone 100% Java. It’s hip, the recruiters who use “grep” to evaluate resumes seem to like it, and, best of all, there’s nothing hard enough about Java to really weed out the programmers without the part of the brain that does pointers or recursion, so the drop-out rates are lower, and the computer science departments have more students, and bigger budgets, and all is well.

The lucky kids of JavaSchools are never going to get weird segfaults trying to implement pointer-based hash tables. They’re never going to go stark, raving mad trying to pack things into bits. They’ll never have to get their head around how, in a purely functional program, the value of a variable never changes, and yet, it changes all the time! A paradox!

They don’t need that part of the brain to get a 4.0 in major.

Am I just one of those old-fashioned curmudgeons, like the Four Yorkshiremen, bragging about how tough I was to survive all that hard stuff?

Heck, in 1900, Latin and Greek were required subjects in college, not because they served any purpose, but because they were sort of considered an obvious requirement for educated people. In some sense my argument is no different that the argument made by the pro-Latin people (all four of them). “[Latin] trains your mind. Trains your memory. Unraveling a Latin sentence is an excellent exercise in thought, a real intellectual puzzle, and a good introduction to logical thinking,” writes Scott Barker. But I can’t find a single university that requires Latin any more. Are pointers and recursion the Latin and Greek of Computer Science?

Now, I freely admit that programming with pointers is not needed in 90% of the code written today, and in fact, it’s downright dangerous in production code. OK. That’s fine. And functional programming is just not used much in practice. Agreed.

But it’s still important for some of the most exciting programming jobs. Without pointers, for example, you’d never be able to work on the Linux kernel. You can’t understand a line of code in Linux, or, indeed, any operating system, without really understanding pointers.

Without understanding functional programming, you can’t invent MapReduce, the algorithm that makes Google so massively scalable. The terms Map and Reduce come from Lisp and functional programming. MapReduce is, in retrospect, obvious to anyone who remembers from their 6.001-equivalent programming class that purely functional programs have no side effects and are thus trivially parallelizable. The very fact that Google invented MapReduce, and Microsoft didn’t, says something about why Microsoft is still playing catch up trying to get basic search features to work, while Google has moved on to the next problem: building Skynet^H^H^H^H^H^H the world’s largest massively parallel supercomputer. I don’t think Microsoft completely understands just how far behind they are on that wave.

But beyond the prima-facie importance of pointers and recursion, their real value is that building big systems requires the kind of mental flexibility you get from learning about them, and the mental aptitude you need to avoid being weeded out of the courses in which they are taught. Pointers and recursion require a certain ability to reason, to think in abstractions, and, most importantly, to view a problem at several levels of abstraction simultaneously. And thus, the ability to understand pointers and recursion is directly correlated with the ability to be a great programmer.

Nothing about an all-Java CS degree really weeds out the students who lack the mental agility to deal with these concepts. As an employer, I’ve seen that the 100% Java schools have started churning out quite a few CS graduates who are simply not smart enough to work as programmers on anything more sophisticated than Yet Another Java Accounting Application, although they did manage to squeak through the newly-dumbed-down coursework. These students would never survive 6.001 at MIT, or CS 323 at Yale, and frankly, that is one reason why, as an employer, a CS degree from MIT or Yale carries more weight than a CS degree from Duke, which recently went All-Java, or U. Penn, which replaced Scheme and ML with Java in trying to teach the class that nearly killed me and my friends, CSE121. Not that I don’t want to hire smart kids from Duke and Penn — I do — it’s just a lot harder for me to figure out who they are. I used to be able to tell the smart kids because they could rip through a recursive algorithm in seconds, or implement linked-list manipulation functions using pointers as fast as they could write on the whiteboard. But with a JavaSchool Grad, I can’t tell if they’re struggling with these problems because they are undereducated or if they’re struggling with these problems because they don’t actually have that special part of the brain that they’re going to need to do great programming work. Paul Graham calls them Blub Programmers.

It’s bad enough that JavaSchools fail to weed out the kids who are never going to be great programmers, which the schools could justifiably say is not their problem. Industry, or, at least, the recruiters-who-use-grep, are surely clamoring for Java to be taught.

But JavaSchools also fail to train the brains of kids to be adept, agile, and flexible enough to do good software design (and I don’t mean OO “design”, where you spend countless hours rewriting your code to rejiggle your object hierarchy, or you fret about faux “problems” like has-a vs. is-a). You need training to think of things at multiple levels of abstraction simultaneously, and that kind of thinking is exactly what you need to design great software architecture.

You may be wondering if teaching object oriented programming (OOP) is a good weed-out substitute for pointers and recursion. The quick answer: no. Without debating OOP on the merits, it is just not hard enough to weed out mediocre programmers. OOP in school consists mostly of memorizing a bunch of vocabulary terms like “encapsulation” and “inheritance” and taking multiple-choice quizzicles on the difference between polymorphism and overloading. Not much harder than memorizing famous dates and names in a history class, OOP poses inadequate mental challenges to scare away first-year students. When you struggle with an OOP problem, your program still works, it’s just sort of hard to maintain. Allegedly. But when you struggle with pointers, your program produces the line Segmentation Fault and you have no idea what’s going on, until you stop and take a deep breath and really try to force your mind to work at two different levels of abstraction simultaneously.

The recruiters-who-use-grep, by the way, are ridiculed here, and for good reason. I have never met anyone who can do Scheme, Haskell, and C pointers who can’t pick up Java in two days, and create better Java code than people with five years of experience in Java, but try explaining that to the average HR drone.

But what about the CS mission of CS departments? They’re not vocational schools! It shouldn’t be their job to train people to work in industry. That’s for community colleges and government retraining programs for displaced workers, they will tell you. They’re supposed to be giving students the fundamental tools to live their lives, not preparing them for their first weeks on the job. Right?

Card Punch -- yes, I learned Fortran on one of these when I was 12.Still. CS is proofs (recursion), algorithms (recursion), languages (lambda calculus), operating systems (pointers), compilers (lambda calculus) — and so the bottom line is that a JavaSchool that won’t teach C and won’t teach Scheme is not really teaching computer science, either. As useless as the concept of function currying may be to the real world, it’s obviously a prereq for CS grad school. I can’t understand why the professors on the curriculum committees at CS schools have allowed their programs to be dumbed down to the point where not only can’t they produce working programmers, they can’t even produce CS grad students who might get PhDs and compete for their jobs. Oh wait. Never mind. Maybe I do understand.

Actually if you go back and research the discussion that took place in academia during the Great Java Shift, you’ll notice that the biggest concern was whether Java was simple enough to use as a teaching language.

My God, I thought, they’re trying to dumb down the curriculum even further! Why not spoon feed everything to the students? Let’s have the TAs take their tests for them, too, then nobody will switch to American Studies. How is anyone supposed to learn anything if the curriculum has been carefully designed to make everything easier than it already is? There seems to be a task force underway (PDF) to figure out a simple subset of Java that can be taught to students, producing simplified documentation that carefully hides all that EJB/J2EE crap from their tender minds, so they don’t have to worry their little heads with any classes that you don’t need to do the ever-easier CS problem sets.

The most sympathetic interpretation of why CS departments are so enthusiastic to dumb down their classes is that it leaves them more time to teach actual CS concepts, if they don’t need to spend two whole lectures unconfusing students about the difference between, say, a Java int and an Integer. Well, if that’s the case, 6.001 has the perfect answer for you: Scheme, a teaching language so simple that the entire language can be taught to bright students in about ten minutes; then you can spend the rest of the semester on fixed points.

Feh.

I’m going back to ones and zeros.

(You had ones? Lucky bastard! All we got were zeros.)

Are you a Junior in college who can rip through a recursive algorithm in seconds, or implement linked-list manipulation functions using pointers as fast as you can write on the whiteboard? Check out our summer internships in New York City! Applications are due February 1st.

Saporasaki

Joel on Software - In JapaneseThe Japanese version of Joel on Software (the book) is out! I want to thank Yasushi Aoki who translated it and Ohmsha, the publisher.

So far, the book is available in English, Italian, Korean, Chinese, and Japanese. I’m certain the Japanese translation will be excellent because Yasushi-san worked closely with me on many small points of translation and worked very hard to correctly translate all those weird cultural references and inside jokes I always put in my articles. The quality of the physical book is much nicer than the English original (why do they have such nice paper in Japan?)

One of my dad’s books on educational linguistics was translated to Japanese when I was a kid. I got a primer on Japanese and deciphered the cover, and was thrilled that our name in Japanese is usually pronounced sa-po-ra-sa-ki, which sounds like a cross between an alcoholic drink and a rad motorcycle.

UPDATE Paul Jensen emails: “As a student of linguistics and Japanese, I feel I should point out that your last name in Japanese is actually su-po-ru-su-ki. In Japanese, /u/ is generally silent following /s/, so your name would come out as [sporuski], which is pretty close to how it’s pronounced in English.”

Interesting Links

I can’t figure out who’s writing it, but Enplaned, a blog about commercial aviation, is one of the best blogs I’ve come across in a long time, full of insightful, in-depth articles about the airline industry.

RedditReddit is a website with a cute alien mascot, featuring user-submitted and user-rated links. If you’ve already seen the Aardvark’d movie you saw the developers of reddit, Steve Huffman and Alexis Ohanian.

TWiTI’ve subscribed and unsubscribed to a lot of Podcasts trying to keep entertained on the subway, and the bottom line is that there are only two I can tolerate on a regular basis: TWiT and VentureVoice. TWiT made me understand why Leo Laporte was so beloved on Tech TV, and John C. Dvorak will charm your socks off in his role as the cuddly curmudgeon, channeling Jerry Pournelle. VentureVoice gets great interviewees on topics close to my heart and does them with great journalistic values and high production quality… don’t miss the episodes on launching VideoEgg at Demo (#14 and #15).

I had to stop listening to the Gilmore Gang because the sound quality made it inaudible on the streets of New York. (Do they listen to their podcasts after they record them?) I had to stop listening to Adam Curry because all he does is talk about was how great podcasts are. Show us, Adam, don’t tell us. Adam Curry is one of the best DJs in the world, but his show is 10% great DJing, 60% crappy drivel about podcasting, and 30% late-night-infomercial-quality promos for podcasts about garden slugs, followed by Adam talking for 20 minutes about how great it is that there are podcasts about garden slugs. I unsubscribed, and spent the next two hours listening to Jason Robert Brown’s hilarious and uplifting I Could Be In Love With Someone Like You again and again and again.

Shipping Update

We discovered that 2 people working in tandem can ship an order every 12 seconds using the new system. So the actual labor costs are down to 24 seconds per order, in a process which requires literacy and two hands but little else. That gives us a throughput of 2400 orders per day before we have to build a parallel workstation… should last a while.

In an article called In Defense of Not-Invented-Here Syndrome, written in 2001, I said, “[I]f you’ve ever had to outsource a critical business function, you realize that outsourcing is hell. Without direct control over customer service, you’re going to get nightmarishly bad customer service — the kind people write about in their weblogs when they tried to get someone, anyone, from some phone company to do even the most basic thing. If you outsource fulfillment, and your fulfillment partner has a different idea about what constitutes prompt delivery, your customers are not going to be happy, and there’s nothing you can do about it, because it took 3 months to find a fulfillment partner in the first place, and in fact, you won’t even know that your customers are unhappy, because they can’t talk to you, because you’ve set up an outsourced customer service center with the explicit aim of not listening to your own customers.”

How to Ship Anything

When we started selling the Aardvark DVDs on our website, we didn’t think it would be so hard to pack and ship them. First of all, we didn’t expect there would be so many, and we conveniently forgot some of the little problems like filling out customs forms which turned out to be big nightmares.

For the first batch that we shipped we did things in a very ad-hoc way. I used Microsoft Word’s Mail Merge feature to print many of the labels; for international orders I wrote a little Access application that filled out forms on the USPS web site, and so on.

The actual shipping turned out to be quite difficult. It was amazing how small ergonomic problems turn into huge headaches when you’re shipping thousands of orders to 60 different countries. For example, the customs form generated by the USPS web site needed to be signed. We had tons of these. We tried to “sign” them by running them through the laser printer again and printing a couple of signature images in the correct place. This worked OK on one printer, but every other printer in the office jammed up when you gave it paper that had already come out of a laser printer. Why is that? Eventually, of course, the printer which could print signatures ran out of toner. It was a Dell. Dell doesn’t let office products stores sell their toner. The only way to get toner was to order it from Dell and pay a lot of money for overnight delivery.

My estimate was that it took about three minutes of labor per order, and we only got it that low because we were batching them up. Worse, it took someone skilled in SQL queries and Mail Merge to create the labels.

Last weekend, I thought, there’s got to be an easier way. I spent some time researching tools on the Internet for making this stuff easier, and on Monday, I started building our new Super Duper Shipping System, aka FogShip, which took about three days to set up, program, and debug.

Here’s what the physical system looks like.

The whole process is controlled by a single computer running Microsoft Access. The shipping data itself lives in SQL Server.

Every day when you’re ready to ship things, you click on a button in Access which causes a stack of packing slips to print out. A typical packing slip is shown at right.

Why Microsoft Access? I was skeptical myself. I’ve never, ever used the Access “report” feature in my life; I always imagined it was intended for old-fashioned people who need to have their data printed out for some obscure reason and can’t fathom the idea of looking things up on a computer.

Well, it turns out that Access reports are perfect for generating packing slips. This is what they were born to do. Since the data is already in SQL Server, it took no more than a couple of hours to layout and design the report exactly the way I wanted it.

Two other things you’ll notice about our packing slips, learned from hard experience. First of all, instead of printing them on a standard-sized sheet of paper, I printed them on 5×8 cards. This has two big advantages. First, they fit into the envelopes we use for shipping most books, CD-ROMs, and DVDs without folding. This really makes a difference when you have a lot of shipping to do. Second, because the card stock is a little bit stiffer than paper, it’s easier to shove them into the envelope.

You’ll also notice the bar code printed in the top corner. There are many different schemes for printing barcodes, but if you’re printing your own barcodes, the easiest barcode system to use is something called Code 39. Other barcode systems want you to add a checksum at the end of the barcode and that means you have to write a little bit of code to calculate the checksum. But Code 39 embeds the the error correction protocol in each letter, which means that you can get a Code 39 font that works just like a font without writing any software.

When you’re ready to start shipping you grab a packing slip and scan it.

I didn’t really know which barcode scanner to buy, and no matter how hard I tried, I couldn’t find any online reviews of barcode scanners. I did find out that the kind of scanner I wanted was a USB keyboard wedge scanner. That means that it has a USB port, which makes connection simple, and it behaves exactly like a keyboard as far as your computer is concerned. I also found out that I wanted a laser scanner, not a CCD scanner, because it works at a distance. I ended up buying a Wasp WLS-9000 along with the stand which you can see in the picture. So far it’s been great.

Setting up the barcode reader was the easiest thing in the world. Plug it in to a USB port, and you’re done. It comes with a whole book of barcodes that you can scan to configure it to behave in many interesting ways, which you can throw away. Without any setup, when you scan a barcode, your scanner will type the letters that barcode represents on the keyboard and press Enter.

In FogShip, scanning the barcode launches a VBA process which does three things.

First, it marks the order as shipped in our online database, so the customer gets an email to let them know their order is on the way. Technically speaking, it would be more accurate to tell them their order shipped when the post office actually came by and picked up their order, but that enhancement will take more programming and will have to wait.

The second thing that happens when you scan the barcode is that it displays the information from the packing slip on a second large screen in a giant font. That way you can immediately shove the packing slip into the envelope… you don’t have to keep it around any more just to keep track of what you’re packing. This was a lesson learned from the first round of shipping, where we had to keep the packing slip outside the envelope until the last minute so that we would know what to pack and what the shipping method was; keeping the packing slip out until the last minute was just one more annoying thing that made the whole process less fluid.

Third, it prints a mailing label, and, for international orders, a customs slip.

To print USPS mailing labels with postage, you really only have three choices. You can use the USPS website (“Click ‘n’ Ship”), but that is designed for home users shipping one or two items. It is not designed to be automated and it always prints lots of instructions which you throw away. Since it relies on Adobe Acrobat Reader for the label, the process of printing is cumbersome and doesn’t work well unless you use expensive, full page 8.5 x 11 labels from a laser printer. Anyway, Click ‘n’ Ship is not the way to go.

The other two choices are Stamps.com and Endicia. Both of them have ways to programmatically print a label.

Endicia had a couple of advantages over Stamps.com for our application: it had a decent tool for laying out custom forms and labels exactly the way you wanted, and it had all the documentation on their website. Stamps.com required you to sign up for a developer program, so it took a while to get the documentation we needed, and we needed that thing yesterday, so we just went with Endicia.

Endicia’s system for printing labels couldn’t be easier. You create an XML file with all the shipping information and launch their app with the name of that XML file on the command line. It spits out a label and exits, creating a new XML file with tracking information and status information. The stamps.com system, as far as I know, is based on ActiveX, which would have worked fine for our application, too.

Now… let’s talk about spitting out a label. If you’re doing more than three or four labels a day, you’ll want a label printer, and if you want the whole process to go quickly, you’ll want a really fast label printer, not a laser printer that has to warm up. Everybody recommended Zebra which makes high-end, industrial strength label printers. We went with the Z4Mplus, which spits out a 4×6 label in less than a second. Not only that, it peels the label for you if you get the optional peeler (sign me UP!) This thing is really, really cool. And if you get Direct Thermal labels, you don’t need to buy a ribbon or ink or toner or anything… it sort of burns the print onto the label.

Next problem: customs forms for international orders. A customs form looks like this:

The green bit on the left is a sticky label which you peel off and stick to the envelope. The white part is just plain paper, attached via perforation. You have to fill out the whole thing with details like price, an exact description of the products, how much they weigh, and you have to sign both sides. Filling these things out is a pain in the butt.

The first trick was to find a printer that could print on 4″ x 7″ paper, the size of the customs forms, without messing up the sticky label part. It also had to be able to print pretty close to the edge of the page, it couldn’t depend on manually feeding each form, and it had to look good enough so that an image of the signature wouldn’t look too fake. It had to be fast at printing and not take a long time warming up, or it would slow down the process.

The printer we found which met all these criteria was the HP LaserJet 2420. We bought two of them (the other one prints the packing cards). It can hold about 100 blank forms in the envelope tray… it turned out that these forms are almost exactly the size of Monarch envelopes which is a standard format for most programs.

The setup software that comes with this printer is completely awful and crashed trying to install on a brand new Windows XP computer. It took several hours of fooling around with the drivers before this thing would print properly. In the meantime, it was printing, but super-slowly, and we couldn’t figure out why. I’m pretty shocked that in this day and age HP is still shipping printers that just can’t be set up by normal humans.

Back to Microsoft Access, where I created a report for the customs form. Using a ruler I carefully measured the position of each of the fields on the paper form and set up the Access report accordingly. It worked great, even producing a decent facsimile of a signature on the signature line!

The last step in the process is attaching any other flotsam, like Airmail or Priority Mail stickers.

And, tada! You’re done.

Shipping an international order now takes about 35 seconds, down from 3 minutes, and can be done by anyone, whether or not they have SQL and Mail Merge skills. Domestic orders are even faster since they don’t need customs forms. Most of all, it’s all really fun.

News

We have this big empty loft next door where we’re about to start construction on another eleven offices. In the meantime it’s serving as a temporary ping-pong room.

Fog Creek Goes to ETech

We gave all the Fog Creek programmers a choice of which conference to attend this spring, from a long list of about 12 conferences, and all of them wanted to go to ETech. So that’s where you’ll find us next March. It was incredibly interesting last year — most of the presentations, especially the ones that weren’t given by the sponsors, were great, but the best part was interacting with the interesting attendees.