Archive for the ‘software’ Category

Unpacking Packets

Saturday, April 27th, 2013

So, I sort of understand this whole TCP thing: You open a connection, you send packets, you close the connection. TCP provides a reliable delivery protocol layered on top of the unreliable IP protocol. So your data gets wrapped in a TCP segment, which gets wrapped in an IP datagram.

But what does that actually look like?

Web requests, email, and all of that add another layer of protocol overhead on top of TCP, so let’s start out with something really simple: the world’s dumbest instant messaging service. We’re going to use netcat, the Swiss army knife of TCP/IP utilities. All we’re going to do is have one netcat process (the server) listen on a TCP port, and have another netcat process (the client) open a connection to it. Both will send any messages typed on the command line, and print any messages they get. We start up the server like so:

$ nc -l 43981

That’s just telling netcat to start up and listen on port 43981. Why 43981? We’ll get to that in a bit.

Then we switch to another terminal, and start up the client like so:

$ nc localhost 43981

Here, we need to tell it which server to connect to, and give it the same port number. Then we type stuff into the client:

$ nc localhost 43981
hello world!
how's it going?

Each time we hit return, the line shows up in the server:

$ nc -l 43981
hello world!
how's it going?

A key thing about TCP is that it’s a two-way connection. Part of what the client does when it opens the connection is tell the server how to send messages back to it. So here we can also type something into the server:

$ nc -l 43981
hello world!
how's it going?
pretty good!

And it will show up in the client:

$ nc localhost 43981
hello world!
how's it going?
pretty good!

When we get bored, we ctrl-c to quit either the server or the client, and the other shuts down automatically.

Pop the Hood

Ok, so that’s it. Messages going across a TCP connection a line at a time. Totally bare-bones. So what’s going on under the hood? To answer that, we’re going to re-run this little exercise, and this time we’re going to use tcpdump to listen in on the conversation. As the name implies, tcpdump listens in on TCP traffic and dumps it out to the screen. So, open a third terminal and fire up tcpdump:

$ sudo tcpdump -i lo -X port 43981
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 65535 bytes

“-i lo” tells it to listen on the loopback interface, since our machine is just sending messages to itself, and “-X” will dump out the TCP segments in a couple of useful formats. “port 43981″ tells it to only report traffic to and from our netcat server port.

We don’t see anything when we start up our netcat server, but as soon as we start up the client, we get this in the tcpdump terminal:

12:50:53.227362 IP localhost.59356 > localhost.43981: Flags [S], seq 586457076, win 32792, options [mss 16396,sackOK,TS val 48581958 ecr 0,nop,wscale 7], length 0
	0x0000:  4500 003c d2b2 4000 4006 6a07 7f00 0001  E..<..@.@.j.....
	0x0010:  7f00 0001 e7dc abcd 22f4 9ff4 0000 0000  ........".......
	0x0020:  a002 8018 fe30 0000 0204 400c 0402 080a  .....0....@.....
	0x0030:  02e5 4d46 0000 0000 0103 0307            ..MF........
12:50:53.227404 IP localhost.43981 > localhost.59356: Flags [S.], seq 2685804629, ack 586457077, win 32768, options [mss 16396,sackOK,TS val 48581958 ecr 48581958,nop,wscale 7], length 0
	0x0000:  4500 003c 0000 4000 4006 3cba 7f00 0001  E..<..@.@.<.....
	0x0010:  7f00 0001 abcd e7dc a016 2055 22f4 9ff5  ...........U"...
	0x0020:  a012 8000 fe30 0000 0204 400c 0402 080a  .....0....@.....
	0x0030:  02e5 4d46 02e5 4d46 0103 0307            ..MF..MF....
12:50:53.227439 IP localhost.59356 > localhost.43981: Flags [.], ack 1, win 257, options [nop,nop,TS val 48581958 ecr 48581958], length 0
	0x0000:  4500 0034 d2b3 4000 4006 6a0e 7f00 0001  E..4..@.@.j.....
	0x0010:  7f00 0001 e7dc abcd 22f4 9ff5 a016 2056  ........"......V
	0x0020:  8010 0101 fe28 0000 0101 080a 02e5 4d46  .....(........MF
	0x0030:  02e5 4d46                                ..MF

What we see here is the client and server negotiating a TCP connection in what’s known as a three-way handshake. Our client sends a packet saying that it wants to start a connection, the server sends back an acknowledgement, and the client responds with a confirmation. For each of these we get a summary line describing the packet and then a dump of the actual contents – verbatim, byte-by-byte. The “0×0000″ and such on the left are the byte index in hexadecimal for the start of each row; so zero, 10 (16 in decimal), 20 (32), 30 (48). The big chunk in the center is the data in hex characters. Each hex character is 4 bits (half a byte, and thus referred to as a “nibble” – ah, nerd humor), so each set of 4 is two bytes. The block on the right is the same data, rendered as ASCII characters (with all the non-printing characters shown as periods). Since what we’re dealing with here is all binary data, that’s not useful yet.

So what is all this crap?

IP Header

Well, like I said, we’ve got TCP segments wrapped in IP datagrams, so the IP data is going to be what we see first. As always, Wikipedia is an awesome resource, and its page on IPv4 lays out the datagram structure for us bit-by-bit. (You may want to open that up in another tab for reference while you’re reading this.) Let’s look at our dump of the first packet:

	0x0000:  4500 003c d2b2 4000 4006 6a07 7f00 0001  E..<..@.@.j.....
	0x0010:  7f00 0001 e7dc abcd 22f4 9ff4 0000 0000  ........".......
	0x0020:  a002 8018 fe30 0000 0204 400c 0402 080a  .....0....@.....
	0x0030:  02e5 4d46 0000 0000 0103 0307            ..MF........

The first thing we see is the “4″ telling us that this is an IPv4 datagram (not IPv6). Then a “5″ for the header length. That’s in 32-bit words, so each of those will be two blocks of hex characters. So we already know that the IP header part of this packet is just:

	0x0000:  4500 003c d2b2 4000 4006 6a07 7f00 0001  E..<..@.@.j.....
	0x0010:  7f00 0001                                ....

The next byte is the DSCP and ECN fields. They’re all zeros, so we can ignore them here, but essentially they tell routers how important or urgent this packet is. In principle, all packets are the same, but in practice we might want some packets – like for Voice Over IP – to have a higher priority if there’s a lot of traffic. This one byte opens a rabbit hole of technical and policy issues.

The next two bytes – 003c or 60 in decimal – tell us the total length of the packet. Sure enough, the packet ends after 12 bytes of the “0×0030″ row. Two bytes here means that the total length of the packet can’t be more than 65535 bytes (2^16 – 1).

Each packet incurs a certain amount of overhead in transmission and processing, so it makes sense to put as much data in each packet as possible. But while the IPv4 protocol sets a maximum of 65535 bytes, it doesn’t require that every router support that. Remember that the protocol was developed back when 64K bytes was more than most machines had, and even now, that’s a lot for one message on a router that’s handling large volumes of traffic.

So our next four bytes – d2b2 4000 – deal with fragmentation. When a router has to forward a datagram that’s bigger than the next router can deal with, it will break it into fragments. The first thing we need is an Identification field so the server knows which fragments go together. The starting index is arbitrary – d2b2 for this one – but you’ll see that it’s incremented normally for later packets. Why not just start with 0001 or 0000? I suspect there’s another rabbit hole there, and I’d guess it has to do with managing multiple connections between the same client and server.

The other two bytes are divided up a little oddly: 3 bits for flags and 13 bits for the Fragment Offset – its index number. That means that the flags are the 8, 4, and 2 bits of the first nibble. It’s 4, so that’s the Don’t Fragment bit. Even if we were fragmented, this is the first packet, so the Fragment Offset is zero.

Next is TTL – Time To Live. When I bring up this page in a browser on my laptop, it sends packets skipping across the network to my hosting provider in California. They’ll pass through a few routers at my ISP and several more at internet backbone providers across the country before they get to the hosting server. There isn’t a pre-ordained route that they’ll follow. Each router looks at each packet and tries to figure out where to send it to get it closer to its destination. This is what makes the internet robust: If one of those connections goes down, the router will figure out the next best way to get the message through. (And yes, the mechanics of how that works are more than other whole essay.)

The downside of this is that if one or more routers are mis-configured, they could send the packets back to a previous router, and they’d end up going in loops. To keep packets from circling endlessly, they have a limited lifespan, measured in “hops”. Each router along the way decrements the TTL field. If the packet hasn’t got where it’s going by the time it gets to zero, the router knows something’s gone wrong, and drops it. Our packet starts off with a TTL byte of 40, so it’s got 64 hops to live.

It may not look like it, but we’re almost down to the end of the IP header here. The next byte tells us the IP Protocol number for the contents of this IP datagram. 06 means it’s TCP.

The next two bytes – 6a07 – are the header checksum. It’s a number calculated from all the bytes in the header. It’s a way to check that the header wasn’t garbled in transit. When a router gets a packet, it calculates a checksum based on the header it received; if any bits got randomly flipped, the checksums won’t match. (This doesn’t protect against intentional tampering because someone could also update the checksum.)

The last two fields are the source and destination IP addresses. Again, this is a two-way connection we’re setting up here, so the client needs to tell the server where to send packets back to. Since we’re just talking to ourselves over the loopback interface, they’re both 127.0.0.1 – 7f00 0001 in hex.

TCP Header

Ok, that’s the IP header. Now on to the TCP header. Let’s strip the IP header out of our packet and see what’s left.

	0x0000:
	0x0010:            e7dc abcd 22f4 9ff4 0000 0000      ....".......
	0x0020:  a002 8018 fe30 0000 0204 400c 0402 080a  .....0....@.....
	0x0030:  02e5 4d46 0000 0000 0103 0307            ..MF........

The first two fields – two bytes each – are the source and destination port numbers: e7dc and abcd. That’s why I picked the weird port to run this on: 43981 in hex is abcd, so it’s easy to spot in the output. e7dc is 59356, which isn’t significant – it’s just what was automatically chosen when the client opened the connection. Perhaps the most significant thing about the ports is that they’re not part of the IP header. Ports are a TCP-level concept; the IP layer only cares about getting the packets to the right machine.

The next four bytes – 22f4 9ff4 – are the Sequence Number (586,457,076). As with the Fragment Offset in the IP layer, this is to keep track of what order the segments belong in and which have been received. The big difference is that here it’s the index of the starting byte in the segment, so it will increase from segment to segment by the number of bytes in the TCP data. It also starts at an arbitrary value, and loops around to zero when it hits the maximum Sequence Number (4 Gigabytes). More on this later.

The next number is the Acknowledgement Number. It’s essentially the Sequence Number for the data received. It’s zero for now, so we’ll talk about it later when it’s got something to say for itself.

The next nibble is the Data Offset, which is the TCP header length in 32-bit words. It’s “a” (10), for a total of 40 bytes, which matches what we can see.

The rest of the a002 block are unused bits (reserved for future use) and flags. They’re all zero except the 2 bit, which is the SYN flag (for synchronize), which means that this segment is the start of a connection.

The next two bytes – 8018 (32792 in decimal) – are the Window Size. This is the sender putting a cap on how much data can be sent back to it, in case it has limited resources. I don’t know the reason for that exact number, but there’s a surprise here: We’ll see in a minute that there’s an optional field that multiplies this value.

Next is the TCP checksum. Unlike the IP checksum, this one is summed across both the TCP header and data. Why doesn’t the IP checksum just do both? I’m not sure, but I’d guess there’s both a design principle and a practical reason. TCP shouldn’t really depend on IP for that. Even though they were designed to work together, they have separate responsibilities. In theory, you could run TCP on top of other protocols than IP, though I don’t know of anyone doing that. So if TCP has to calculate its own checksum, there’s no point making IP do it as well. The practical concern is that the TCP data can be huge compared to the 40 bytes of IP header, and the IP checksum has to be checked at every hop; the TCP checksum is only checked when it reaches its destination.

The last standard field is the Urgent Pointer. The URG flag wasn’t set, so this is 0000. As to when that flag is set and how the urgent pointer is used when it is, that’s probably yet another rabbit hole.

Beyond that, we have a number of optional fields. They’re odd in that they’re not in a specific order, they’re different sizes, and they may have multiple sub-fields. The first byte of each tells us what type of field it is. I’ll run through them quickly, putting pipes between the sub-fields so you can see how they’re broken up.

  • 02|04|400c: Maximum segment size = 400c (16,396 bytes). This is used by the TCP layer to limit the segment size and save it from getting fragmented at the IP layer.
  • 04|02: Selective acknowledgement permitted. Allows the receiver to request re-transmission of only missing segments, rather than the whole message. More on this later.
  • 08|0a|01fd b1be|0000 0000: Timestamp=01fd b1be (33403326), previous timestamp=0000 0000. Used to help determine the order of the TCP segments when the amount of data being sent is more than the maximum Sequence Number.
  • 01: no operation – padding to align options on word boundaries for performance.
  • 03|03|07: window scale = 7; multiplies Window Size by 2^7, bringing it to 4,197,376 bytes

We can check our homework here by looking at the packet summary. (Come on, it wouldn’t have been any fun if we did that first!)

12:50:53.227362 IP localhost.59356 > localhost.43981: Flags [S], seq 586457076, win 32792, options [mss 16396,sackOK,TS val 48581958 ecr 0,nop,wscale 7], length 0

Now that we know what we’re looking at, it’s pretty easy to read: time; host.port for source and destination; SYN flag; Sequence Number; window size; options with max segment, selective ack, timestamp, no-op, and window scale; and data length of zero.

Awesome, done! That’s the first packet.

The Rest of the Handshake

Now that we have the structure down, we just need to look at what’s different about the rest of the packets, and we can get most of what we need from the summary lines.

So, packet two is the response from our server.

12:50:53.227404 IP localhost.43981 > localhost.59356: Flags [S.], seq 2685804629, ack 586457077, win 32768, options [mss 16396,sackOK,TS val 48581958 ecr 48581958,nop,wscale 7], length 0
	0x0000:  4500 003c 0000 4000 4006 3cba 7f00 0001  E..<..@.@.<.....
	0x0010:  7f00 0001 abcd e7dc a016 2055 22f4 9ff5  ...........U"...
	0x0020:  a012 8000 fe30 0000 0204 400c 0402 080a  .....0....@.....
	0x0030:  02e5 4d46 02e5 4d46 0103 0307            ..MF..MF....

What’s different? The IP identification is 0000, which strikes me as odd. Don’t know what’s going on there. And the checksum is different because of that. If we were connecting two different machines, we’d have seen the source and destination addresses switch. In the TCP header, the ports switched, which is the tip-off that this packet is going from the server to the client. We have a new Sequence Number, since the client and server keep separate counts of the data bytes they send. We now have an Acknowledgement Number, which is the client’s Sequence Number from the last packet, plus one. Both the SYN and ACK flags are set, marking this as the server acknowledgement. There’s a slightly different Window Size, but with the same scaling factor. The previous timestamp is set. And of course a different TCP checksum because of all that.

The third packet is the client’s confirmation of the connection. The server knows that the client asked for a connection, and the server knows that it sent an acknowledgement, but it needs to know that the client got the acknowledgment.

12:50:53.227439 IP localhost.59356 > localhost.43981: Flags [.], ack 1, win 257, options [nop,nop,TS val 48581958 ecr 48581958], length 0
	0x0000:  4500 0034 d2b3 4000 4006 6a0e 7f00 0001  E..4..@.@.j.....
	0x0010:  7f00 0001 e7dc abcd 22f4 9ff5 a016 2056  ........"......V
	0x0020:  8010 0101 fe28 0000 0101 080a 02e5 4d46  .....(........MF
	0x0030:  02e5 4d46                                ..MF

The IP identity has been incremented, which changes the checksum. The TCP Sequence Number has been incremented and matches Acknowledgement Number from the previous server packet. Likewise, the Acknowledgement Number is now the Sequence Number from the server packet, incremented. We have fewer options – just the timestamps and a couple of no-ops – so the Header Size is only 8. Only the ACK flag is set, which says that the connection is solid now. The Window Size is 257, with no scaling factor in the options. I don’t think this is actually used now that the connection is established, so I don’t know why it’s not zero. Something else to research.

Anyway, hey, TCP connection established! So this is the first thing we’d see whether we’re sending email, hitting a web page, or whatever.

Getting Down to Work

After that, we send a message from the client to the server, and get a response back. This time, we’re actually sending data!

12:51:04.418321 IP localhost.59356 > localhost.43981: Flags [P.], seq 1:14, ack 1, win 257, options [nop,nop,TS val 48584755 ecr 48581958], length 13
	0x0000:  4500 0041 d2b4 4000 4006 6a00 7f00 0001  E..A..@.@.j.....
	0x0010:  7f00 0001 e7dc abcd 22f4 9ff5 a016 2056  ........"......V
	0x0020:  8018 0101 fe35 0000 0101 080a 02e5 5833  .....5........X3
	0x0030:  02e5 4d46 6865 6c6c 6f20 776f 726c 6421  ..MFhello.world!
	0x0040:  0a                                       .
12:51:04.418446 IP localhost.43981 > localhost.59356: Flags [.], ack 14, win 256, options [nop,nop,TS val 48584755 ecr 48584755], length 0
	0x0000:  4500 0034 6d10 4000 4006 cfb1 7f00 0001  E..4m.@.@.......
	0x0010:  7f00 0001 abcd e7dc a016 2056 22f4 a002  ...........V"...
	0x0020:  8010 0100 fe28 0000 0101 080a 02e5 5833  .....(........X3
	0x0030:  02e5 5833                                ..X3

Here’s where the ASCII output finally becomes useful. You can spot the “hello world!” content right away, which makes it a lot easier to keep track of which packets are which as we’re digging through this.

In the first packet, the Total Length is bigger by 13 (“hello world!” plus the return character). The client set the PSH flag, to indicate that there’s data to push to the application (netcat). The Acknowledgement and Sequence numbers are the same as last time because no data was sent; but then in the server’s response, its Acknowledgement Number is 13 (no coincidence) more than the client’s Sequence Number.

Ok, so now that the Acknowledgement number is starting to move for real, let’s talk about what all the futzing around with it and the Sequence Number is about. This is really the core of TCP, what makes it special. This is how it guarantees that the data gets through even when IP delivery fails and packets get dropped. To do that, the client needs to keep track of each chunk of data it sends out, and it needs to get a response from the server saying that piece has been received. It’s like registered mail but better, because the response tells the client not only that the server got a packet, but how much data it got and where it is in the client’s data set. (The Acknowledgement Number is actually the number of the next byte the server expects to get from the client).

TCP isn’t normally a one-for-one exchange like this. Often, the client would send out a whole mess of packets at once. Rather than acknowledging each individually, which would generate a whole lot of traffic, the server just sends back the Acknowledgement Number for the highest packet received, assuming it gets them all. If it doesn’t, if there are packets missing, it could send an acknowledgement for the highest contiguous packet it gets, and have the client re-send everything later. But it could be smarter than that, and this is where the Selective Acknowledgement option comes in. That lets the server acknowledge several discontinuous blocks (as start and end bytes), so the client only has to re-send the missing pieces.

Also remember that this is a two-way conversation. When the server is sending an acknowledgement to the client, it’s also sending its own Sequence Number, so the client can keep track of what it’s received from the server. In this case, all the content is in the outbound message, and what’s coming back is an empty acknowledgement. But in an HTTP request, we’d see content in the outbound message – request headers, the type of request (GET, POST, etc.), the path to the web page we’re requesting, and any form data – and the response would have the HTML content of the web page.

Shutting Down

The one thing left to show you is what happens when we close the connection. If you hit ctrl-c in either terminal, you’ll see both the client and server exit immediately, but you’ll also see a bunch of traffic in tcpdump.

12:51:38.214610 IP localhost.59356 > localhost.43981: Flags [F.], seq 30, ack 14, win 257, options [nop,nop,TS val 48593204 ecr 48590359], length 0
	0x0000:  4500 0034 d2b7 4000 4006 6a0a 7f00 0001  E..4..@.@.j.....
	0x0010:  7f00 0001 e7dc abcd 22f4 a012 a016 2063  ........"......c
	0x0020:  8011 0101 fe28 0000 0101 080a 02e5 7934  .....(........y4
	0x0030:  02e5 6e17                                ..n.
12:51:38.215934 IP localhost.43981 > localhost.59356: Flags [F.], seq 14, ack 31, win 256, options [nop,nop,TS val 48593205 ecr 48593204], length 0
	0x0000:  4500 0034 6d13 4000 4006 cfae 7f00 0001  E..4m.@.@.......
	0x0010:  7f00 0001 abcd e7dc a016 2063 22f4 a013  ...........c"...
	0x0020:  8011 0100 fe28 0000 0101 080a 02e5 7935  .....(........y5
	0x0030:  02e5 7934                                ..y4
12:51:38.215994 IP localhost.59356 > localhost.43981: Flags [.], ack 15, win 257, options [nop,nop,TS val 48593205 ecr 48593205], length 0
	0x0000:  4500 0034 d2b8 4000 4006 6a09 7f00 0001  E..4..@.@.j.....
	0x0010:  7f00 0001 e7dc abcd 22f4 a013 a016 2064  ........"......d
	0x0020:  8010 0101 fe28 0000 0101 080a 02e5 7935  .....(........y5
	0x0030:  02e5 7935                                ..y5

We’ve sent a couple more messages back and forth (“how’s it going?”, “pretty good!”), so the Sequence and Acknowledgement numbers have jumped ahead a bit, as have the timestamps.

The real action here is in the TCP flags, the 2nd byte in the 0×0020 row. The ACK bit is still set, but now the FIN bit is too. That’s the client telling the server to close the connection. The server sends back a response with the FIN bit set, and the client sends a simple acknowledgement. It’s the same send-acknowledge-confirm exchange that we saw in the opening handshake.

Wrapping Up, Moving On

Ok, so that’s been a lot of to absorb, but what I hope you’ve gotten out of this is that all these internet protocol details are interestingly complex, but totally comprehensible. You’ve got the tools to look under the hood, and with a bit of patience you can figure out what all the parts are doing.

If you haven’t actually run through this little netcat/tcpdump exercise on your own terminal, give it a try. You don’t need to pick through it byte-by-byte like I have. Just take a couple minutes to watch the packets go back and forth, and skim the summary lines. That gave me a sort of visceral sense of what’s going on.

If you do want to dig into this more, try pointing tcpdump at a real service. Set up a minimal web page on your local web server, point tcpdump at port 80, and hit the page with your browser. I just tried that myself, and I think it’s going to keep me busy for a while.

Thanks to Frank Hunleth for corrections to the original version of this post.

Business Process Automation

Monday, January 24th, 2011

If you’re not familiar with the term, it probably sounds like some marketing bullshit, but it’s actually a good shorthand description of one of the ways that software can be insanely useful. To break it down: “Business process” is a blanket term for all the things that people have do at work to get something done. You’ll have a business process for setting up new customer accounts, fulfilling an order for widgets, or whatever. The “Automation” part is about writing software to make this more efficient. So “Business Process Automation” covers any software that makes people’s jobs easier.

That doesn’t mean replacing people with machines. It’s about letting the computer handle the tedious, repetitive crap, and freeing people up to work on the more interesting stuff. In almost any business process, there will be some amount of straightforward bookkeeping that a computer can handle; but there will also be a need for judgement and common sense, and the ability to deal with ambiguous or unforeseen situations, which computers are fundamentally incapable of.

The simplest, most common example of this is spreadsheet software. A computer can’t tell you what numbers and formulas to put in, or what the results mean—what’s important, what action to take—but it can save you the hassle of re-doing the math every time something changes. It’s a power tool, like a band saw or a nail gun.

The first time this was really driven home for me was years ago, when I was working at a little web development shop. I was chatting with a friend of mine who was a designer there, and she was griping that she’d gotten stuck with this crappy assignment. One of our clients had these tables of financial data on their site that had to be updated every month. They sent the new data to us in a PDF document. Her task was to copy the data from the PDF into HTML. So she opened up the PDF in one window, and the HTML source in another, and copied it one entry at a time: click, drag, ctrl-c, click, ctrl-v; for row after row, table after table. She’d just finished the first batch of this, and it had taken her nearly two full days. Absolutely mind-numbing, and she was going to have to do this every month.

She told me all this, and I said, “You did what? No, that’s crazy; don’t do that again. Give me a little while.” I went off and found a utility to dump out the PDF as text, then wrote a Perl script to parse the data out and stuff it into an HTML template. It took me maybe a day and a half. Now, each month, she just had to run the script and spend maybe fifteen minutes proof-reading the pages it spat out.

This is a tiny example, but it demonstrates the alchemy you can work with software. I got to do this fun little side-project; I freed up nearly 10% of her time, so she could do more valuable work for the company; and I made her job way less sucky. Everybody wins.

Business Software

Thursday, January 20th, 2011

I’m a programmer. I write business software. Programmers hate writing business software. It’s not cool, and it’s hard. Programmers won’t come out and say that it’s hard; they’ll use terms like “messy” or “uninteresting”. It’s not hard for technical reasons; it’s hard for human reasons. And we, on the whole, are not good with humans. I’m not great with them, but I’m increasingly of the opinion that they’re more interesting than machines.

Let me give you a definition of Business Software that’ll shed a bit of light on the situation: Business Software is software that isn’t used by programmers. It’s used by non-technical people, suits. It’s not useful to us. We don’t care about it. Programmers use programming tools and play video games; end of story. In fact, if the suits think it’s great, that’s a sign that it sucks.

Since we don’t use business software, we don’t know what it should do, whether it’s working right, or how to make it better. There isn’t a good, objective definition of what it’s supposed to do; it’s “whatever the user wants.” Sometimes, it’s based on bizarre business, organizational, historical, or legal requirements. There’s no way to reason it out. If you try, you’ll often find out that your perfectly logical conclusion is in fact disastrously wrong. So we have to ask the users how the software needs to work. That means sitting in meetings. With suits.

Since the users aren’t technical, they can’t just tell you what they want. They probably don’t know, themselves. They don’t know what the limits of the technology are. They have no idea what’s feasible. They ask all sorts of really dumb questions, and expect the impossible. They’re not good at visualizing how they’ll actually use the software. They’re usually wrong about what they think they want. And they will change their minds. So you have to bridge that gap, and you don’t get to just meet them halfway. They might be able to learn a few basic technical things, but you need to learn a lot about their business. So now you have to be the one asking a lot of dumb questions.

So just figuring out what your program needs to do is the really hard bit. You’d think that after suffering through all of that, you’d at least get to do something really cool, but no – the technical solution often turns out to be fairly straightforward, usually something involving a web server and a database. I think that’s why there’s such frantic innovation in tools and frameworks for building web applications: There are all these programmers out there trying to make the technical work interesting.

In fact, to write successful business software, you have to make it deliberately uncool. You don’t use development builds of cutting edge tools and elaborate frameworks; you use simple, stable tools that lots of other people know how to use. In business software, you’re really trying to program yourself out of a job, to make yourself replaceable. You’re trying to take all of this business knowledge, and embody it in code. I’ve heard programmers brag that the guys who came after them weren’t smart enough to maintain their code. That’s completely backwards. If you’re really good, anyone should be able to pick up your code and understand what it’s doing and why. Code is communication, the expression of an idea. Your code should make all of the complex business logic clear and precise. If you’ve done really well, you can even walk non-technical people through it and give them a better understanding of their own business. If other programmers can’t work with it, you either didn’t really understand it, or you didn’t communicate it clearly.

“Legacy code” is a term of distain. It’s shorthand for “crusty old software written in some boring language on an obsolete platform.” In practical terms, it’s anything more than about 6 months old. Real legacy code can even be decades old, written back in the days of Big Iron. It’s been tweaked and stretched and hacked up ever since. It has years of business logic layered on like shellac. To work with it, you have to waste valuable brain space learning about how people did things before we Figured It Out. If you’re really skilled and really lucky, your reward will be that your business software becomes legacy code. It’ll become critical to the business and they’ll use it for years to come. The fullness of time will expose all of the mistakes and misjudgements that you made. You will learn humility, and it will make you a better programmer. It’s a tough love kinda thing.

So why do I write business software? Because I like people. I like trying to figure them out. I like trying to get my head inside their world. All of that stupid, illogical business logic? Most of it really does make sense once you learn the context and history of it. I like that it’s unintuitive; I like that little jolt of surprise I get when I learn that the world is more complex than I’d thought. I also like being useful. Just on the level of pure self-preservation, I figure I’m better off being useful rather than just clever. But I also like the feedback. I get all kinds of warm fuzzies from writing a bit of code that makes someone else’s life a little easier. I’ve come to the conclusion that I was put on this earth to program the Suck out of people’s jobs.

Software Pin Factory

Friday, January 19th, 2007

I started reading Adam Smith’s The Wealth of Nations. For fun. Because I’m a big dork. Right at the beginning, he talks about division of labor and pin factories. Pins: little bits of wire with a point on on end and a knob on the other. The point is that a single craftsman, without particular skill or tools, could only make a handful of pins a day. In a pin factory, the process is divided into more than a dozen stages, each simple and specific, with its own tools. This process is literally hundreds of times more productive. Businesses have been trying to pull off this trick ever since.

It worked fantastically well in the industrial age. As we moved into more of a service economy, the principle still applied, though not as strongly. In your typical office, you have specialization of skills: Managers, admin assistants, sales, marketing, and so on. In a one person company, that one person can cover all those roles. Things will go smoother when they can divide the work up among several people, but not by a factor of hundreds.

As software development has become a bigger and bigger business, the obvious thing has been to try to apply the same assembly line model: analyze, subdivide, specialize. And so we come to Enterprise Software development, which pretty much follows the old waterfall model: Requirements, architecture, design, implementation, test, support. The business analysts don’t write code, the architect doesn’t write test plans, and the programmers don’t field support calls. The business analyst writes a requirements document, the architect writes an architecture document, and so on. Each stage does their piece and throws it over the wall to the next team. The principle is sound: The more narrowly you divide up the skill set, the more specialized each group will be, and the better at their particular task. Push this far enough and eventually you can replace them with trained monkeys. Very clean, very elegant. Very efficient? Umm… no.

The problem is that there’s a significant difference between an Enterprise Software solution and a pin. A pin is a very simple thing. Everyone knows how it works and can tell at a glance if a given pin will do its job. Unless it’s bent, blunt or missing its head, it’s good to go. If an end user finds that one pin is poorly made, they just throw it away and get another.

An Enterprise Software system is not simple or obvious. Aside from the fact that even the customer often doesn’t entirely understand how they want it to work until they start using it, the amount of information that has to be communicated from stage to stage is huge. Requirements documents can be hundreds of pages. None of the workers has any innate sense of whether the information handed to them from the previous stage is complete or accurate. Every misunderstanding is multiplied down the chain. It’s like playing “telephone” with War and Peace.

The key here is that when you divide up the labor in a process, you also incur a communications cost between each stage. It’s a trade-off: Each stage can be done more efficiently, but information about the work has to be passed between them. In the pin factory, the benefit of specialization is huge, and the communications transfer is tiny – as I said, the quality of a pin is obvious. In a software shop, the benefit of specialization may be significant, but the communications overhead is enormous. To come at it from a different angle, I’ve heard it said that a programmer is lucky if they get to spend 20% of their time actually writing code. The other 80% is communications overhead: reading documents, sitting in meetings, writing documents. Add in the time you spend on rework due to miscommunication, and that 20% starts to sound generous. Here, the assembly line is actually less efficient.

So what do you do? The development of “Agile” tools and techniques is a response to this, along with component or service oriented architectures. Break the system up into independent, well-defined pieces, and put them in front of the users as quickly as possible. The catch here is that you can’t use trained monkeys. You need people who can talk to the end users and write code, who can carry the product all the way through the process. Maybe they need help from experts like domain specialists or QA engineers, maybe they can divvy up some of the work to tech writers or programmers, but the key is to capture an end-to-end understanding of the product in as few heads as possible.

Under the Hood

Friday, April 22nd, 2005

Imagine that you work in a large and somewhat old-fashioned office building. If you want to send a message to your buddy Joe over at XYZ Corp, this is how it goes. You write out your letter on a piece of paper and put a sticky note on it saying, “Please send to Joe Smith at XYZ Corp,” and hand it to your secretary. She (I said this was an old-fashioned place) puts the letter in an envelope and puts Joe Smith’s name on it. Then she looks up the address for XYZ Corp and writes that on the envelope, along with your return address. Then she hands it off to the guys in the mail room.

What they do is interesting. They look at the address for XYZ Corp and say, “Hmm… that’s out of town. It needs to go to Central.” So they put your envelope inside another envelope and write “Central Post Office” on it.

When it gets to the central post office, they open the outer envelope and read the address on your letter. They say, “Oh, this is going to Chicago,” or wherever. So they put your envelope inside another envelope and write “Central Post Office, Chicago” on it.

Then it gets to the Central Post Office in Chicago. They open up the envelope addressed to them and see the address for XYZ Corp. So they put it in another envelope that just has the 9-digit zip code for the XYZ Corp building on it.

It shows up at XYZ Corp, and the guys in their mail room open up that envelope and see that it’s addressed to Joe Smith. Somebody runs it upstairs to Joe’s secretary, and she opens the envelope and hands Joe your letter. When Joe sends a reply back, it works the same way.

This is how the Internet works.

It’s actually more complicated – there are more middlemen – but that’s fundamentally how it all works. It’s all these little letters (called “packets”) flying around a very, very fast postal system. This is a pretty clear match for email, but it’s also how everything from web pages to streaming video to Voice Over IP works.

When you “go to” a web site, you’re really mailing out a request for a web page. It’s like writing off to a mail-order catalog company. There’s a standard form that defines how you ask for web pages. You fill it out and send it in. You’re sending this little form that says, “I want to see http://www.bluegraybox.com/index.html”. That request goes out through this metaphorical postal system to the bluegraybox.com server, and some little toiling minion there xeroxes off another copy of the index.html document and mails it back to you.

Like I said, it’s more complicated than that. How do you keep email and web pages and FTP sites all running on the same machine without tripping over each other? Imagine your office building has a bunch of different departments in it, but they all share the same mail room. Instead of a billing department and a sales department, you’ve got an email department and a web department and an FTP department. The way it works is that they each have different post office box numbers. Whenever a letter comes into your building, the mailroom guys just have to drop it in the right box. One of the rules of this postal system is that the addresses on letters have to have a box number. Furthermore, these box numbers are standardized, so that box 80 is the normal box number for the web department, box 25 is for email, etc. So when you send off your web page request, you know that it’s a web page request, and wherever it’s going, it should be going to box 80.

This is also how you keep your responses straight. Even if you’re the only guy at your company, you could be downloading a couple of mp3s in the background while you’re popping up new browser windows right and left. If all that stuff is landing in the same inbox, you’ll never sort it out. So each time you ask for a web page, you set up a new post office box just for its responses. Your downloads go to boxes 5001 and 5002, and your web pages end up in 5003, 5004, and so on.

Here’s the next wrinkle. Say you send off a request for some big, fat mp3 file that won’t all fit in one envelope. So it gets broken up into a whole bunch of separate letters. Now on top of that, like the real post office, stuff can get lost: Mail trucks get stolen; Some yutz in New Jersey cuts through a long-distance fiber-optic cable with a backhoe. Even if nothing gets lost, there’s no guarantee that everything is going to show up in the order you sent it.

So what do you do? First, you send a letter that effectively says, “Hey, I want to send a whole bunch of letters back and forth with you. Here’s the address you should send all the replies to.” This is where your web browser says, “Connecting to …” in the status bar. Once you’ve got an OK back, you say, “Send me that mp3 file.” You get a whole flood of letters back, numbered “1 of 23″, “2 of 23″ and so on. You count through them and realize that you’re missing number 17, so you send another message saying to re-send it. When you finally have all the letters, you can put them in the right order, open them up, and glom the mp3 file together.

This little procedure we’re going through here is called a Protocol. It’s not part of the postal system itself, but it’s a set of rules that people have agreed on for how to use the postal system. In this case, it’s a way of communicating reliably through a system that isn’t reliable. It’s a layer of communications on top of a layer of communications. It’s very meta.

The internet is built up of layers of these protocols. Like the envelopes inside envelopes, at each stage, you’re only concerned with the outermost layer. You slip on or peel off your envelope, and everything else is just the stuff inside it, be it one layer or many. IP, the Internet Protocol, is the postal system – simple, but not entirely reliable. TCP, the Transmission Control Protocol, provides the reliable, ordered delivery on top of IP. Email (SMTP), the web (HTTP), and others are actually another layer on top of TCP. Essentially, they all define standard forms for different mail-order requests.

Again, it’s even more complicated than that. But for now, lots and lots of little letters zipping back and forth across the world. That’s the way to think of it.