protocol-evolution.adoc 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
  1. = GPSD-NG: A Case Study in Application Protocol Evolution
  2. Eric S. Raymond <esr@thyrsus.com>
  3. v1.5.3, Jan 2021
  4. :author: Eric S. Raymond
  5. :description: A case study in the evolution of the gpsd protocol
  6. :email: <esr@thyrsus.com>
  7. :keywords: GPSD, protocol, evolution
  8. :robots: index,follow
  9. :sectlinks:
  10. :toc: macro
  11. include::inc-menu.adoc[]
  12. This document is mastered in asciidoc format. If you are reading it in HTML,
  13. you can find the original at the GPSD project website.
  14. == Introduction
  15. GPSD is a service daemon that collects data from serial and USB GPS
  16. sensors attached to a host machine and presents it in a
  17. simple-to-parse form on TCP/IP port 2947. This is a less trivial task
  18. than it sounds, because GPS sensor interfaces are both highly variable
  19. and really badly designed (see http://esr.ibiblio.org/?p=801[Why GPSes
  20. suck, and what to do about it] for a description of NMEA 0183 and
  21. other horrors).
  22. In this paper, however, we will be ignoring all the dodgy stuff that
  23. goes on at GPSD's back end to concentrate on what happens at the front
  24. - the request-response protocol through which client programs get
  25. access to the information that GPSD acquires from its devices and
  26. internal computations.
  27. The GPSD request-response protocol is entering its third generation of
  28. design, and I think the way it has evolved spotlights some interesting
  29. design issues and long-term trends in the design of network protocols
  30. in general. To anticipate, these trends are: (1) changing tradeoffs
  31. of bandwidth economy versus extensibility and explicitness, (2) a
  32. shift from lockstep conversational interfaces to event streams, (3)
  33. changes in the "sweet spot" of protocol designs due to increasing use
  34. of scripting languages, and (4) protocols built on metaprotocols.
  35. Carrying these trends forward may even give us a bit of a glimpse at
  36. the future of application-protocol design.
  37. == The first version: a simple conversational protocol
  38. The very first version of GPSD, back in the mid-1990s, handled
  39. NMEA GPSes only and was designed with a dead-simple request-response
  40. protocol. To get latitude and longitude out of it, you'd connect
  41. to port 2947 and have a conversation that looked like this:
  42. -------------------------------------------------------------------------
  43. -> P
  44. <- GPSD,P=4002.1207 07531.2540
  45. -------------------------------------------------------------------------
  46. That is GPSD reporting, the only way it could in the earliest protocol
  47. version, that I'm at latitude about 40 north and 75 west.
  48. If you are a mathematician or a physicist, you're probably noticing
  49. some things missing in this report. Like a timestamp, and a circular
  50. error estimate, and an altitude. In fact, it was possible to get some
  51. these data using the old protocol. You could make a compound request
  52. like this:
  53. -------------------------------------------------------------------------
  54. -> PAD
  55. <- GPSD,P=4002.1207 07531.2540,A=351.27,D=2009:07:11T11:16Z
  56. -------------------------------------------------------------------------
  57. For some devices (not all) you could add E and get error estimates.
  58. Other data such as course and rate of climb/sink might be available
  59. via other single-letter commands. I say "might be" because in those
  60. early days gpsd didn't attempt to compute error estimates or velocities
  61. if the GPS didn't explicitly supply them. I fixed that, later, but
  62. this essay is about protocol design so I'm going to ignore all the
  63. issues associated with the implementation for the rest of the discussion.
  64. The version 1 protocol is squarely in the tradition of classic textual
  65. Internet protocols, even though it doesn't look much like (say) SMTP
  66. transactions - requests are simple to emit and responses are easy to
  67. parse. It was clearly designed with the more specific goal of
  68. minimizing traffic volume between the daemon and its clients. It
  69. accomplishes that goal quite well.
  70. == The second version: from conversational to streaming
  71. However, when I started work on it it in 2004 there was already
  72. pressure from the existing userbase to change at least one of the
  73. protocol's major assumptions - that is, that the client would poll
  74. whenever it wanted data. It's usually more convenient to be able to
  75. say to the daemon "Speak!" and have it stream TPV
  76. (time/position/velocity) reports back at you at the sensor's sampling
  77. rate (usually once per second). Especially when, as with GPSD, you
  78. have a client library that can spin in a thread picking up the updates
  79. and dropping them in a struct somewhere that you specify.
  80. This was the first major feature I implemented. I called it "watcher
  81. mode", and it required me to add two commands to the protocol. There
  82. were already so many single-shot commands defined that we were close
  83. to running out of letters for new ones; I was able to grab "W" for the
  84. command that enables or disables watcher mode, but was left with the
  85. not-exactly-intuitive "O" for the streaming TPV report format. Here's
  86. how it looks:
  87. -------------------------------------------------------------------------
  88. -> W=1
  89. <- GPSD,W=1
  90. <- GPSD,O=MID2 1118327700.280 0.005 46.498339529 7.567392712 1342.392 36.000 32.321 10.3787 0.091 -0.085 ? 38.66 ? 3
  91. <- GPSD,O=MID2 1118327701.280 0.005 46.498339529 7.567392712 1342.392 48.000 32.321 10.3787 0.091 -0.085 ? 50.67 ? 3
  92. <- GPSD,O=MID2 1118327702.280 0.005 46.498345996 7.567394427 1341.710 36.000 32.321 10.3787 0.091 -0.085 ? 38.64 ? 3
  93. <- GPSD,O=MID2 1118327703.280 0.005 46.498346855 7.567381517 1341.619 48.000 32.321 10.3787 0.091 -0.085 ? 50.69 ? 3
  94. <- GPSD,Y=MID4 1118327704.280 8:23 6 84 0 0:28 7 160 0 0:8 66 189 45 1:29 13 273 0 0:10 51 304 0 0:4 15 199 34 1:2 34 241 41 1:27 71 76 42 1:
  95. <- GPSD,O=MID2 1118327704.280 0.005 46.498346855 7.567381517 1341.619 48.000 32.321 10.3787 0.091 -0.085 ? ? ? 3
  96. -> W=0
  97. <- GPSD,W=0
  98. -------------------------------------------------------------------------
  99. The fields in the O report are tag (an indication of the device
  100. sentence that produced this report), time, time error estimate,
  101. longitude, latitude, altitude, horizontal error estimate, vertical
  102. error estimate, course, speed, climb/sink, error estimates for
  103. those last three fields, and mode (an indication of fix quality). If
  104. you care about issues like reporting units, read the documentation.
  105. The 'Y' report is a satellite skyview, giving right-ascension,
  106. declination, and signal quality for each of the visible satellites.
  107. GPSes usually report this every five cycles (seconds).
  108. The 'W', 'O' and 'Y' sentences, together, effectively constituted
  109. version 2 of the protocol - designed for streaming use. The other
  110. single-shot commands, though still supported, rapidly became
  111. obsolescent.
  112. Attentive readers may wonder why I designed a novel 'O' format rather
  113. that writing the watcher-mode command so that it could specify a
  114. compound report format (like PADE) every second. Part of the answer
  115. is, again, that we were running out of letters to associate with new
  116. data fields like the error estimates. I wanted to use up as little of
  117. the remaining namespace as I could get away with.
  118. Another reason is, I think, that I was still half-consciously thinking
  119. of bit bandwidth as a scarce resource to be conserved. I had a bias
  120. against designs that would associate "extra" name tags with the
  121. response fields ("A=351.27") even though the longest tagged response
  122. GPSD could be expected to generate would still be shorter than a
  123. single Ethernet packet (1509 bytes).
  124. == Pressure builds for a redesign
  125. Along about 2006, despite my efforts to conserve the remaining
  126. namespace, we ran out of letters completely. As the PADE example
  127. shows, the protocol parser interprets command words letter
  128. by letter, so trying to wedge longer commands in by simple
  129. fiat wouldn't work. Recruiting non-letter characters as
  130. command characters would have been ugly and only postponed
  131. the problem a bit, not solved it.
  132. 'H' is actually still left, but at the time I believed we couldn't
  133. commit the last letter (whatever it was) because we'd need it as an
  134. inline switch to a new protocol. I started feeling pressure to
  135. actually design a new protocol. Besides running out of command
  136. namespace in the old one, a couple of things were happening that
  137. implied we'd need to define new commands.
  138. What had used up the last of the command namespace was multi-device
  139. support. Originally, GPSD could only monitor one GPS at a time. I
  140. re-engineered it so it could monitor multiple GPSes, with GPS streams
  141. available as data channels to which a client could connect one at a
  142. time. I was thinking about use cases like this one: spot two GPSes on
  143. either end of an oil tanker, use the position delta as a check on
  144. reported true course.
  145. (For those of you wondering, this wasn't the huge job it may sound
  146. like. I had carefully structured GPSD as a relatively small (about
  147. 5.5 KLOC) networking and dispatcher top-level calling a 30 KLOC driver
  148. and services library, all of which was designed from the get-go to use
  149. re-entrant structures. Thus, only the top layer needed to change, and
  150. at that only about 1 KLOC of it actually did. Building the test
  151. framework to verify the multi-device code in action was a bigger job.)
  152. Note that the "one at a time" limitation was imposed by the
  153. protocol design, notably the fact that the 'O' record didn't contain
  154. the name of the device it was reporting from. Thus, GPSD could not
  155. mix reports from different devices without effectively discarding
  156. information about where they had come from.
  157. Though I had just barely managed to cram in multi-GPS support without
  158. overrunning the available command space, we were starting to look at
  159. monitoring multiple *kinds* of devices in one session - RTCM2
  160. correction sources and NTRIP were the first examples. (These are both
  161. protocols that support
  162. http://www.esri.com/news/arcuser/0103/differential1of2.html[differential
  163. GPS correction].) My chief lieutenant was muttering about making GPSD
  164. report raw pseudorange data from the sensors that allow you to get at
  165. that. It was abundantly clear that broadening GPSD's scope was going
  166. to require command-set extensions.
  167. Even though I love designing application protocols only a little bit
  168. less than I love designing domain-specific minilanguages, I dragged my
  169. feet on tackling the GPSD-NG redesign for three years. I had a strong
  170. feeling that I didn't understand the problem space well enough, and
  171. that jumping into the effort prematurely might lock in some mistakes
  172. that I would come to gravely regret later on.
  173. == JSON and the AISonauts
  174. What finally got me off the dime in early 2009 were two developments - the
  175. push of AIS and the pull of JSON.
  176. AIS is the marine http://www.navcen.uscg.gov/enav/ais/[Automatic
  177. Identification System]. All the open-source implementations of AIS
  178. packet decoding I could find were sketchy, incomplete, and not at a
  179. quality level I was comfortable with. It quickly became apparent that
  180. this was due to a paucity of freely available public information about
  181. the applicable standards.
  182. http://esr.ibiblio.org/?p=888[I fixed that problem] - but having done
  183. so, I was faced with the problem of just how GPSD is supposed to
  184. report AIS data packets to clients in a way that can't be confused
  185. with GPS data. This brought the GPSD-NG design problem to the front
  186. burner again.
  187. Fortunately, my AIS-related research also led me to discover
  188. http://www.json.org/[JSON], aka JavaScript Object Notation. And JSON
  189. is *really nifty*, one of those ideas that seem so simple and
  190. powerful and obvious once you've seen it that you wonder why it wasn't
  191. invented sooner.
  192. In brief, JSON is a lightweight and human-readable way to serialize
  193. data structures equivalent to Python dictionaries, with attributes
  194. that can be numbers, strings, booleans, nested dictionary objects,
  195. or variable-extent lists of any of these things.
  196. == GPSD-NG is born
  197. I had played with several different protocol design possibilities
  198. between 2006 and 2009, but none of them really felt right. My
  199. breakthrough moment in the GPSD-NG design came when I thought this:
  200. "Suppose all command arguments to GPSD-NG commands, and their
  201. responses, were self-describing JSON objects?"
  202. In particular, the equivalent of the 'O' report shown above looks like
  203. this in GPSD-NG (with some whitespace added to avoid hard-to-read
  204. linewraps):
  205. -------------------------------------------------------------------------
  206. {"class":"TPV","tag":"MID50","device":"/dev/pts/1",
  207. "time":"2005-06-09T14:35:11.79",
  208. "ept":0.005,"lat":46.498333338,"lon":7.567392712,"alt":1341.667,
  209. "eph":48.000,"epv":32.321,"track":60.9597,"speed":0.161,"climb":-0.074,
  210. "eps":50.73,"mode":3}
  211. -------------------------------------------------------------------------
  212. To really appreciate what you can do with object-valued attributes,
  213. however, consider this JSON equivalent of a 'Y' record. The skyview
  214. is a sublist of objects, one per satellite in view:
  215. -------------------------------------------------------------------------
  216. {"class":"SKY","tag":"MID2","device":"/dev/pts/1",
  217. "time":"2005-06-09T14:35:11.79",
  218. "reported":8,"satellites":[
  219. {"PRN":23,"el":6,"az":84,"ss":0,"used":false},
  220. {"PRN":28,"el":7,"az":160,"ss":0,"used":false},
  221. {"PRN":8,"el":66,"az":189,"ss":40,"used":true},
  222. {"PRN":29,"el":13,"az":273,"ss":0,"used":false},
  223. {"PRN":10,"el":51,"az":304,"ss":36,"used":true},
  224. {"PRN":4,"el":15,"az":199,"ss":27,"used":false},
  225. {"PRN":2,"el":34,"az":241,"ss":36,"used":true},
  226. {"PRN":27,"el":71,"az":76,"ss":43,"used":true}
  227. ]}
  228. -------------------------------------------------------------------------
  229. (Yes, those "el" and "az" attributes are elevation and azimuth. "PRN"
  230. is the satellite ID; "ss" is signal strength in decibels, and "used"
  231. is a flag indicating whether the satellite was used in the current solution."
  232. These are rather more verbose than the 'O' or 'Y' records, but have several
  233. compensating advantages:
  234. * Easily extensible. If we need to add more fields, we just add named
  235. attributes. This is especially nice because...
  236. * Fields with undefined values can be omitted. This means extension
  237. fields don't weigh down the response format when we aren't using them.
  238. * It's explicit. Much easier to read with eyeball than the corresponding
  239. 'O' record.
  240. * It includes the name of the device reporting the fix. This opens up
  241. some design possibilities I will discuss in more detail in a bit.
  242. * It includes, up front, a "class" tag that tells client software what it
  243. is, which can be used to drive a parse.
  244. My first key decision was that these benefits are a good trade for the
  245. increased verbosity. I had to wrestle with this a bit; I've been
  246. programming a long time, and (as I mentioned previously) have reflexes
  247. from elder days that push me to equate "good" with "requiring minimum
  248. computing power and bandwidth". I reminded myself that it's 2009 and
  249. machine resources are cheap; readability and extensibility are the goals
  250. to play for.
  251. Once I had decided that, though, there remained another potential
  252. blocker. The implementation language of gpsd and its principal client
  253. library is C. There are lots of open-source JSON parsers in C out
  254. there, but they all have the defect of requiring malloc(3) and handing
  255. back a dynamic data structure that you then have to pointer-walk at
  256. runtime.
  257. This is a problem, because one of my design rules for gpsd is no use
  258. of malloc. Memory leaks in long-running service daemons are bad things;
  259. using only static, fixed-extent data structures is a brutally effective
  260. strategy for avoiding them. Note, this is only possible because the maximum
  261. size of the packets gpsd sees is fairly small, and its algorithms are O(1)
  262. in memory utilization.
  263. "Um, wait..." I hear you asking "...why accept that constraint when
  264. gpsd hasn't had a requirement to parse JSON yet, just emit it as
  265. responses?" Because I fully expected gpsd to have to parse structured
  266. JSON arguments for commands. Here's an example, which I'll explain fully
  267. later but right now just hint at the (approximate) GPSD-NG equivalent
  268. of a 'W+R+' command.
  269. -------------------------------------------------------------------------
  270. ?WATCH={"raw":1,nmea:true}
  271. -------------------------------------------------------------------------
  272. Even had I not anticipated parsing JSON arguments in gpsd, I try to
  273. limit malloc use in the client libraries as well. Before the
  274. new-protocol implementation the client library only used two calloc(3)
  275. calls, in very careful ways. Now they use none at all.
  276. So my next challenge was to write and verify a tiny JSON parser that
  277. is driven by sets of fixed-extent structures - they tell it what shape
  278. of data to expect and at which static locations to drop the actual
  279. parsed data; if the shape does not match what's expected, error out.
  280. Fortunately, I am quite good at this sort of hacking - the
  281. result, after a day and a half of work, fit in 310 LOC including
  282. comments (but not including 165 LOC of unit-test code).
  283. == Un-channeling: the power
  284. Both gpsd and its C client library could now count on parsing JSON;
  285. that gave me my infrastructure. And an extremely strong one, too;
  286. the type ontology of JSON is rich enough that I'm not likely to ever
  287. have to replace it. Of course this just opened up the next question -
  288. now that I can readily pass complex objects between gpsd and its
  289. client libraries, what do I actually do with this capability?
  290. The possibility that immediately suggested itself was "get rid of channels".
  291. In the old interface, subscribers could only listen to one device at
  292. a time - again, this was a consequence of the fact that 'O' and 'Y'
  293. reports were designed before multi-device support and didn't include a
  294. device field. JSON reports can easily include a device field and
  295. thus need not have this problem.
  296. Instead of a channel-oriented interface, then, how about one where the
  297. client chooses what classes of message to listen to, and then gets
  298. them from all devices?
  299. Note, however, that including the device field raises some problems of
  300. its own. I do most of my gpsd testing with a utility I wrote called
  301. gpsfake, which feeds one or more specified data logs through pty
  302. devices so gpsd sees them as serial devices. Because X also uses pty
  303. devices for virtual terminals, the device names that a gpsd instance
  304. running under gpsfake sees may depend on random factors like the
  305. number of terminal emulators I have open. This is a problem when
  306. regression-testing! I thought this issue was going to require me
  307. to write a configuration command that suppresses device display; I
  308. ended up writing a sed filter in my regression-test driver instead.
  309. Now we come back to our previous example:
  310. -------------------------------------------------------------------------
  311. ?WATCH={"raw":true,nmea:true}
  312. -------------------------------------------------------------------------
  313. This says: "Stream all reports from all devices at me, setting raw
  314. mode and dumping as pseudo-NMEA if it's a binary protocol." The way to
  315. add more controls to this is obvious, which is sort of the point --
  316. nothing like this could have fit in the fixed-length syntax of the old
  317. pre-JSON protocol.
  318. This is not mere theory. At the time of writing, the ?WATCH command is
  319. fully implemented in gpsd's Subversion repository, and I expect it to
  320. ship ready for use in our next release (2.90). Total time to build
  321. and test the JSON parsing infrastructure, the GPSD-NG parser, and the
  322. gpsd internals enhancements needed to support multi-device listening?
  323. About a working week.
  324. Just to round out this section, here is an example of what an
  325. actual AIS transponder report looks like in JSON.
  326. -------------------------------------------------------------------------
  327. {"class"="AIS","msgtype":5,"repeat":0,"mmsi":"351759000","imo":9134270,
  328. "ais_version":0,"callsign":"3FOF8","shipname":"EVER DIADEM",
  329. "shiptype":70,"to_bow":225,"to_stern":70,"to_port":1,"to_starboard":31,
  330. "epfd":1,"eta":05-15T14:00Z,"draught":122,"destination":"NEW YORK",
  331. "dte":0}
  332. -------------------------------------------------------------------------
  333. The above is an AIS type 5 message identifying a ship - giving, among
  334. other things, the ship's name and radio callsign and and destination
  335. and ETA. You might get this from an AIS transceiver, if you had one
  336. hooked up to your host machine; gpsd would recognize those data
  337. packets coming in and automatically make AIS reports available as
  338. an event stream.
  339. == The lessons of history
  340. In the introduction, I called out three trends apparent over time in
  341. protocol design. Let's now consider these in more detail.
  342. === Bandwidth economy versus extensibility and explicitness
  343. First, I noted *changing tradeoffs of bandwidth economy versus
  344. extensibility and explicitness*.
  345. One way you can compare protocols is by the amount of overhead they
  346. incur. In a binary format this is the percentage of the bit stream
  347. that goes to magic numbers, framing bits, padding, checksums, and
  348. the like. In a textual format the equivalent is the percentage
  349. of the bitstream devoted to field delimiters, sentence start and
  350. sentence-end sentinels, and (in protocols like NMEA 0183) textual
  351. checksum fields.
  352. Another way you can compare protocols is by implicitness versus
  353. explicitness. In the old GPSD protocol, you know the semantics of a
  354. request parameter within a request implicitly, by where it is in
  355. the order. In GPSD-NG, you know more explicitly because every parameter is a
  356. name-attribute pair and you can inspect the name.
  357. Extensibility is the degree to which the protocol can have new
  358. requests, responses, and parameters added without breaking old
  359. implementations.
  360. In general, *both extensibility and overhead rise with the degree
  361. of explicitness in the protocol*. The JSON-based TPV record has
  362. has much higher overhead than the O record it replaces, but what
  363. we gain from that is lots and *lots* of extensibility room. We
  364. win three different ways:
  365. * The command/response namespace in inexhaustibly huge.
  366. * Individual requests and responses can readily be extended by adding
  367. new attributes without breaking old implementations.
  368. * The type ontology of JSON is rich enough to make passing arbitrarily
  369. complex data structures through it very easy.
  370. With respect to the tradeoffs between explicitness/extensibility and
  371. overhead, we're at a very different place on the cost-benefit curves
  372. today from when the original GPSD protocol was designed.
  373. Communications costs for the pipes that GPSD uses have
  374. dropped by orders of magnitude in the decade-and-change since GPSD
  375. was designed. Thus, squeezing every last bit of overhead out of the
  376. protocol representation doesn't have the real economic payoff it used to.
  377. Under modern conditions, there is a strong case that implicit,
  378. tightly-packed protocols are false economy. If (as with the first GPSD
  379. protocol) they're so inextensible that natural growth in the
  380. software breaks them, that's a clear down-check. It's better to design
  381. for extensibility up front in order to avoid having to throw out
  382. a lot of work later on.
  383. The direction this points in for the future is clear, especially
  384. in combination with the increasing use of metaprotocols.
  385. === From lockstep to streaming
  386. Second, I noted *a shift from lockstep conversational interfaces to
  387. event streams*.
  388. The big change in the second protocol version was watcher mode. One
  389. of the possibilities this opens up is that you can put the report
  390. interpreter into an asychronous thread that magically updates a C
  391. struct for you every so often, without the rest of your program having
  392. to know or care how that is being done (except possibly by waiting a
  393. mutex to ensure it doesn't read a partially-updated state).
  394. Analogous developments have been visible in other Internet protocols
  395. over roughly the same period. Compare, for example, POP3 to IMAP. The
  396. former is a lockstep protocol, the latter designed for streaming - it's
  397. why IMAP responses have a transaction ID tying them back to the
  398. requesting command, so responses that are out of order due to
  399. processing delays can be handled sanely.
  400. Systems software has generally been moving in a similar direction,
  401. propelled there by distributed processing and networks with unavoidable
  402. variable delays. There is a distant, but perceptible, relationship
  403. between GPSD-NG's request-response objects and the way transactions
  404. are handled within (for example) the X window system.
  405. This trend, too, seems certain to continue, as the Internet becomes
  406. ever more like one giant distributed computing system.
  407. === Type ontology recapitulates trends in language design
  408. Third, *changes in the "sweet spot" of protocol designs
  409. due to increasing use of scripting languages.*
  410. The most exciting thing about JSON to me, speaking as an application
  411. protocol designer, is the rich type ontology - booleans, numbers,
  412. strings, lists, and dictionaries - and the ability to nest them to any
  413. level. In an important sense that is orthogonal to raw bandwidth,
  414. this makes the pipe wider - it means complex, structured data can more
  415. readily be passed through with a minimum of fragile and bug-prone
  416. serialization/deserialization code.
  417. The fact that I could build a JSON parser to unpack to fixed-extent C
  418. structures in 300-odd LOC demonstrates that this effect is a powerful
  419. code simplifier even when the host language's type ontology is limited
  420. to fixed-extent types and poorly matched to that of JSON (C lacks not
  421. only variable-extent lists but also dictionaries).
  422. JSON is built on dictionaries; in fact, every JSON object is a legal
  423. structure literal in the dictionary-centric Python language (with one
  424. qualified exception near the JSON null value). It seems like a simple
  425. idea in 2009, but the apparent simplicity relies on folk knowledge we
  426. didn't have before Perl introduced dictionaries as a first-class data
  427. type (c.1986) and Python built an object system around them (after
  428. 1991).
  429. Thus, GPSD-NG (and the JSON it's built on) reflects and recapitulates
  430. long-term trends in language design, especially those associated with
  431. the rise of scripting languages and of dictionaries as a first-class
  432. type within them.
  433. This produces several mutually reinforcing feedback loops. The
  434. rise of scripting languages makes it easier to use JSON to its full
  435. potential, if only because deserialization is so trivial. JSON will
  436. probably, in turn, promote the use of these languages.
  437. I think, in the future, application protocol designers will become
  438. progressively less reluctant to rely on being able to pass around
  439. complex data structures. JSON distils the standard type ontology of
  440. modern scripting languages (Perl, Python, Ruby, and progeny) into a
  441. common data language that is far more expressive than the structs of
  442. yesteryear.
  443. == Protocols on top of metaprotocols
  444. GPSD-NG is an application of JSON. Not a completely pure one; the
  445. request identifiers, are, for convenience reasons, outside the JSON
  446. objects. But close enough.
  447. In recent years, metaprotocols have become an important weapon in
  448. the application-protocol designer's toolkit. XML, and its
  449. progeny SOAP and XML-RPC, are the best known metaprotocols. YAML
  450. (of which JSON is essentially a subset) has a following as well.
  451. Designing on top of a metaprotocol has several advantages. The most
  452. obvious one is the presence of lots of open-source software to use for
  453. parsing the metaprotocol.
  454. But it is probably more important in the long run that it saves one
  455. from having to reinvent a lot of wheels and ad-hoc representations
  456. at the design level. This effect is muted in XML, which has a weak
  457. type ontology, but much more pronounced in YAML or JSON. As a
  458. relevant example, I didn't have to think three seconds about the right
  459. representation even for the relatively complex SKY object.
  460. == Paths not taken
  461. Following the first public release of this paper, the major questions
  462. to come up from early readers were "Why not XML?" and "Why not a
  463. super-efficient packed binary protocol?"
  464. I would have thought the case against packed binary application
  465. protocols was obvious from my preceding arguments, but I'll make it
  466. explicit here: generally, they are even more rigid and inextensible
  467. than a textual protocol relying on parameter ordering, and hence more
  468. likely to break as your application evolves. They have significant
  469. portability issues around things like byte order in numeric fields.
  470. They are opaque; they cannot be audited or analyzed without bug-prone
  471. special-purpose tools, adding a forbidding degree of complexity and
  472. friction to the life-cycle maintenance costs.
  473. When the type ontology of your application includes only objects like
  474. strings or numbers that (as opposed to large binary blobs like images)
  475. have textual representations differing little in size from packed
  476. binary, there is no case at all for incurring these large overheads.
  477. The case against XML is not as strong. An XML-based protocol at least
  478. need not be rigidly inextensible and opaque. XML's problem is that,
  479. while it's a good basis for document interchange, it doesn't naturally
  480. express the sorts of data structures cooperating applications want to
  481. pass around.
  482. While such things can be layered over XML with an appropriate schema,
  483. the apparatus required for schema-aware parsing is necessarily
  484. complicated and heavyweight - certainly orders of magnitude more so
  485. than the little JSON parser I wrote. And XML itself is pretty
  486. heavyweight, too - one's data tends to stagger under the bulk
  487. of the markup parts.
  488. == Envoi
  489. Finally, a note of thanks to the JSON developers...
  490. I think JSON does a better job of nailing the optimum in metaprotocols
  491. than anything I've seen before - its combination of simplicity and
  492. expressiveness certainly isn't matched by XML, for reasons already
  493. called out in my discussion of paths not taken.
  494. I have found JSON pleasant to work with, liberating, and
  495. thought-provoking; hence this paper. I will certainly reach for this
  496. Swiss-army knife first thing, next time I have to design an
  497. application protocol.