So I came across another blog post shitting on YAML again. “The Norway Problem”, the author proudly repeats while posting falsehoods. So I looked into it myself, and here’s what I found.
The problems with YAML
YAML™ (rhymes with “camel”) is a human-friendly, cross language, Unicode based data serialization language designed around the common native data types of dynamic programming languages. It is broadly useful for programming needs ranging from configuration files to internet messaging to object persistence to data auditing and visualization.
Over the years, YAML has become quite popular for a lot of things. Depending on how experienced you are as a programmer, you’ve probably seen it before in config files or whatever. It has, in that time, garnered much criticism. There’s generally three big issues, and they kind of play into each other in different ways:
- The specification is unreasonably huge
- Library implementations are all over the place
- The implicit typing (“The Norway Problem”)
Some of this criticism is valid, but some is misplaced. At least, that was my understanding. Let’s take a look.
My own experiences with YAML
In my years programming, I’ve used YAML, TOML, JSON, and others in many projects. I have some choice words about all of these, but I’ll focus on YAML’s part.
The first thing I made that really uses YAML was my custom Python Discord bot, 9 years ago. This was probably my first “major” project that wasn’t contributing to Space Station 131, so I was on my own with much of it. I ended up using YAML for the configuration file. I did not run into any of the commonly-complained about issues with YAML, but I realize this was just luck. I was still using yaml.safe_load() and nothing more.
This does not mean the usage of YAML was without issues. A silly thing about the way my config file worked was that I actually had two files: config.yml, and override.yml. This was because I always needed config.yml loaded to provide the basic structure and default values, and then merged override.yml into it to create the nested datastructure the Python code would access. But is that YAML’s fault? No, it’s not.
I had other serialization issues with the pickle files I was using to persist data. Stupid shit like “defaultdict instances have trouble serializing because they effectively store a lambda.” Nowadays I can look back at this time and realize what the common cause here is: Python. Or, more accurately, dynamic languages in general. It is impossible to correctly do “to/from object” serialization in a dynamic language like Python.
Anyways. Next project. Space Station 14 uses YAML for all “prototypes” (and some other stuff). This means data definitions of entities, recipes, and like 200 other things. Originally the project actually used XML for this, but when I took the reigns I made the decision to switch this to YAML. For the most part, this has been a “great success.” The biggest problem problem we’ve really had is that new contributors sometimes have a hard time realizing it’s whitespace sensitive and then get silly syntax errors. Annoying, but not the end of the world.
How do we avoid “The Norway Problem”? Here’s the secret: while the exact implementation details have varied (we’re at “serv3” now, and it could probably be 4 actually), we’ve always done the actual object deserialization logic with our own code. This means that we take the graph objects (YamlMappingNode, YamlScalarNode, etc) from our library, and we parse them ourselves. We don’t deserialize “no” as a boolean when we shouldn’t, because we only do boolean deserialization when we’re reading a field that needs to take a boolean. Shocking.
This, of course, is the correct way to do object serialization. You serialize against a model set in the program code. This is how we avoid nonsense in Space Station 14, and how I could’ve avoided nonsense in my Discord bot. So when I see these posts about how “bad” YAML is because it might deserialize “no” wrong, all I can think of is “gigantic, utter, dynamically typed skill issue.”
But is this really true? What if I’m wrong? What if we’re using YAML wrong, and the spec demands this behavior? Then I’d surely be making a fool of myself! So… let’s check the damn spec!
What does the spec say
According to their site, YAML has a couple important revisions: 1.0 (January 2004), 1.1 (January 2005), 1.2 (July 2009). Let’s start taking a look at 1.0 to see what’s up.
If you start digging into this, you’ll quickly realize that the spec is light on details about how type conversions and parsing should work. Section 2.4 has this to say:
In YAML, plain (unquoted) scalars are given an implicit type depending on the application. The examples in this specification use types from YAML’s tag repository, which includes types like integers, floating point values, timestamps, null, boolean, and string values.
Following the link, we get:
Following is a description of the three mandatory core tags. YAML requires support for the seq, map and str tags. YAML also provides a set of universal tags, that are not mandatory, in the YAML tag repository available at https://yaml.org/spec/type.html. These tags represent native data types in most programming languages, or are useful in a wide range of applications. Therefore, applications are strongly encouraged to make use of them whenever they are appropriate, in order to improve interoperability between YAML systems.
That last link is dead, but it probably pointed to something similar to Integer Language-Independent Type for YAML™ Version 1.1. Looking at the integer type, for example:
Resolution and Validation:
Valid values must match the following regular expression, which may also be used for implicit tag resolution:
[-+]?0b[0-1_]+ # (base 2) |[-+]?0[0-7_]+ # (base 8) |[-+]?(0|[1-9][0-9_]*) # (base 10) |[-+]?0x[0-9a-fA-F_]+ # (base 16) |[-+]?[1-9][0-9_]*(:[0-5]?[0-9])+ # (base 60)
If we search more in the main spec about implicit typing, we see the following in Section 3.3.2:
The plain scalar style exception allows unquoted values to signify numbers, dates, or other typed data, while quoted values are treated as generic strings. With this exception, a processor may match plain scalars against a set of regular expressions, to provide automatic resolution of such types without an explict[sic] tag.
Okay so just a reminder. The YAML spec does indeed follow RFC 2119 for some keywords like “may”. You may2 have noticed the usage of the word “may” and other weasel words like “depending on the application.” Indeed: my reading of YAML 1.0 agrees that this is fully up to the application.
YAML 1.1 does not seem to deviate much from this, except by making the language involved even more complicated. Same Section 3.3.2:
Tag resolution is specific to the application, hence a YAML processor should provide a mechanism allowing the application to specify the tag resolution rules. […]
It is only in YAML 1.2 that the language gets less weasel-wordy. Now there is a proper concept of “schemas” that really have implicit tag resolution rules, and the “Core Schema” is a “recommended” default:
The Core schema is an extension of the JSON schema, allowing for more human-readable presentation of the same types. This is the recommended default schema that YAML processor should use unless instructed otherwise. It is also strongly recommended that other schemas should be based on it.
But wait! The YAML 1.2 schemas don’t have sexagesimal (base 60) or yes/no booleans! So obviously “The Norway Problem” is not something brought about by this YAML 1.2 recommendation!
Look, I’ve read into this part of the YAML specs a couple times before. I saw the gigantic pile of “may” and “up to the application” and figured “okay, so PyYAML and Ruby and all those other libraries just decided to pick a broken default.” It’s not hard to be reductive like this given that all these libraries have unsafe loading as the default option, so it’s clearly not like they were competently3 designed in the first place.
But still. Is this the extent of it? We can check the older draft specifications! Maybe they’ll tell us something. The December 2001 draft of the spec is the first to have implicit typing written down, and it’s always active, at least from my reading! Fascinating! Skipping ahead by about a year, in the October 2002 draft, the rules seem to have changed again and now it’s “up to the application.” So it seems that during the draft process, they actually changed their minds about this!
Checking the mailing list
“Why” is not an answer you’re going to get in a draft specification, and there sure as hell isn’t a change log. Figuring out where all the spec wording came from is something we can only do by looking at the discussions behind it.
In YAML’s case, this primarily took place on a mailing list in the early 2000s. The archive for this list is thankfully still accessible on SourceForge today. Annoyingly however, the listing on SourceForge has no public archive download (it’s only for project administrators), it’s separated by month, and paginated. This means all the relevant emails (thousands) span a ton of browser tabs.
Digging into this would’ve been impossible without the ability to open these emails in a proper mail client. So I whipped out Python and scraped SourceForge’s goddamn site. If they don’t want me scraping their shit, they should make the public archives they host responsibly accessible. The public viewer only has user names, plaintext message contents, and time values available (no email addresses for privacy reasons), but that’s enough for me to dump stuff into a .mbox file and load it in Thunderbird. My terrible scripts are here and here, if you care. And the mbox files I scraped are here, if you want it.

Yep, that’s a lot of emails
There are thousands of emails, and I only went through a couple percent of them. I was primarily looking for information related to implicit typing and tag resolution: how did it come to be, and what did the authors of the spec intend? Of course, clicking through hundreds of emails while searching for keywords will inevitably mean I get to read a whole lot more than that.
At the core of all this were a few driven people were doing things they wanted to. There was silliness. There were various random new people showing up out of interest. Long debates. One time the spec was offline because the server host was in Peru. This is the first time I’ve ever bothered to “investigate” something like this, and while ancient mailing lists are mostly foreign to me, the vibe still felt somewhat familiar to the communities I’m used to.
Aside: why is YAML “like that”?
Before we go on, I must be clear: I was born in the year 2000; these people were discussing implicit typing rules before I could walk. I was not there for any of it, so I am forced to make my own inferences about the general context this was all happening in.
YAML originates from the XML “hype” cycle from the turn of the millenium. A lot of people and businesses thought XML was magic future juice, because of data interoperability. We put all our data in XML, and now we have a bunch of magic tooling with XSD, XPath, XSLT, and a god knows what other acronyms start with ‘X’. Bloody hell, some companies were selling hardware middleboxes whose sole job was to validate and transform XML, based on more XML! The language was being used for everything: configuration files, serialized state, as a database, RPC protocols, etc.
But like, XML is weird, it’s a markup language. Compare with HTML: if you strip all of the markup from this web page… it’s still somewhat coherent, at least to a human. But do that to a typical use of XML? It’ll lose all meaning. Are we really marking things up?
<u:AddPortMapping xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1">
<NewRemoteHost></NewRemoteHost>
<NewExternalPort>1313</NewExternalPort>
<NewProtocol>UDP</NewProtocol>
<NewInternalPort>1313</NewInternalPort>
<NewInternalClient>192.168.50.3</NewInternalClient>
<NewEnabled>1</NewEnabled>
<NewPortMappingDescription>RobustToolbox UDP</NewPortMappingDescription>
<NewLeaseDuration>0</NewLeaseDuration>
</u:AddPortMapping>
Furthermore, if you’ve ever tried to design an XML format for anything, you’ve probably not been entirely sure whether something should be the tag name, an attribute, or textual content. I’m sure many people have written strongly-opinionated guidelines about the topic, but the fact is that it’s really not intuitive. Also, man, look at the amount of repetition in that example up above. As you probably realize, JSON or YAML do not have these issues. If you’re remotely familiar with either, you’ve had no issues imagining what the above example would’ve looked like in them!
YAML was very much designed to support all the use cases of XML, and that means it was designed with the whole kitchen sink of related tools in mind. Data portability, serialization, config files, bloody everything. Many people had wildly different use cases, and this was reflected by some of the emails I read.
Why is the YAML spec so much more complicated than JSON? Because, well, it wanted to be. There’s acres of spec language about how a “YAML Processor” should work. On the mailing list, there were many drive-by mentions and ideas for “YPATH”, “YAML schemas”, “YAML-RPC”, and much more. People wanted to do with YAML what you can do with XML.
Of course, the XML hype cycle passed, and along went the desire for any of these YAML equivalents. Most of those ambitious goals I mentioned earlier? Never realized. Nowadays, people mostly just use YAML as a nicer alternative to JSON when writing config files. I’ll leave you to be the judge on whether this is a good or bad thing.
I have a lot of thoughts about this myself, and part of the reason I wanted to write this blog post was so that I could add my own take to the internet. Compare some languages, add my own insight and my own opinions, that kinda stuff.
In the end though I was not able to formulate the above in a way I was happy with, and this blog post was languishing for months being otherwise completed. So in the interest of actually publishing things, I’ve cut that out.
If you do want my final opinion on YAML though: it’s alright.
Okay, but what do the emails say about implicit typing?
As I’ve already stated, YAML was originally supposed to have stronger implicit typing rules. They were very aware of the compatibility risks of all of this! In June 2002, it’s clear they were having trouble deciding the exact implicit rules that would be satisfiable and unambiguous for what they wanted. They wanted unquoted strings, but they also wanted implicit integers and floats, maybe dates, and they hadn’t decided yet on booleans. Ideas were thrown around to require qualifying more “niche” types like dates (e.g. ! 2026-05-22), but there was a lot of “that’s ugly for my use case”. The proposal they ended up settling on for the time4 looked something like this:
- 123 # int
- "123" # string
- 1.23 # float
- NaN # string
- (NaN) # float
- + # bool
- no # string
- (no) # bool
- ~ # null
- (null) # null
- 2026-05-22T13:38:08Z # time
- foobar # string
- .foobar # invalid!
- ".foobar" # string
Some of this outdated syntax wasn’t cleaned up in the January 2004 1.0 spec, as one email kindly points out! See if you can spot it!
In September of 2002, the topic of how types work got brought up again. This was prompted by a new person trying out YAML and running into the limitations and risks of the time & date type, and others too became uncomfortable with the existing implicit typing rules. And so the gears started turning, the “DWIM” proposal was made, later renamed to the “unknown types” proposal:
- In a word: this proposal makes YAML DWIM. If you want it to be. If you want to be strict about it, add an explicit transfer method to each node, and/or provide validation/typing/comparison schemas, and so on. If you don’t want to be strict, just treat everything as a string (but always preserve the transfer method).
To my understanding, this proposal then got merged into the 2002 October 31st revision of the spec. Furthermore, they seemed to have been wanting to finish the 1.0 spec sooner rather than later, so the non-core types got moved out so they didn’t need to cut them off at the same time as the rest of the spec. Regardless, at this point in the timeline, I do believe my interpretation is correct: the expectation of the spec authors was that “everything should be strings by default”, and this intent did not change all the way to January 20245. This is reaffirmed in multiple emails by the spec authors, at various points in time:
If Ingy and I ever get the next version of PyYaml finished, the first feature it will have is an “all values are strings” loader.
A standard, non-schema aware Loader should always load values like these as strings. If you want dates as objects, just mix in a date loader class.
This is now possible, because types are out of the spec. They don’t need to be recognized by the parser. This was the smartest thing we did for simplfying YAML logic.
That’s the biggest thing preventing me from deploying it more widely: having to give others elaborate rules about the value syntax. It’s easier to say, “Just start your value with an alphanumeric character and you’ll be OK,” than to say, “All digits bad. YYYY-MM-DD bad. Starting with !’”% bad. t/f/~ bad."
Yeah. This just isn’t the case anymore. Basically every scalar is parsed as a string with a type. And (in the absence of a schema) it is completely up to the Loader as to what to do with that string and type. Loaders are *encouraged* to support the YAML type repository, if and when it makes sense. And most of the time it makes sense to load values as strings by default, and load dates as objects when the user has the load_dates_as_objects option turned on.
Even plain scalars have a type. The type is the empty string. Which is a cue to the Loader to do what makes most sense, based on the Loaders defaults, the Loader options set by the user, or by the typing hints in a Schema document.
What I’m saying is, make your YAML module always load strings by default. No implicit typing at all. That’s the recommended default.
Implicit typing is optional and it is _not_ part of the base standard [1].
[…]
[2] Unfortunately, the current PyYaml does implicit typing when it really isn’t up-to-snuff with the rest of the core spec. This is due to history, when PyYaml was written, implicit types were _not_ optional, and it caused probelms, and the specification was changed from the realization of this msitake (thank you Steve Howell). Hopefully, the new PyYAML which Tim is working on will focus on the core spec first; leaving the data types to another day.
Well, so, I can’t remember where we stand with the ‘y’ and ’n’ booleans. Syck hasn’t supported them because they conflict with other parts of the spec. (See examples 2.21 and 2.24 in either version.)
Which of the following works?
true: y false: n x: 73 y: 129FWIW, I think the y/n boolean implicits are a little bit cute and could be removed.
But there are a few points I would like to remind people of:
- The implicit type repository is not part of the spec. So it follows that they have no relation to 1.1 or 1.2.
- The types are really a recommendation. Domain specific YAML processors are free to define their own implicits.
- The default of a generic YAML processor should be to treat all scalars as strings.
- A good generic processor should not only have an interface to turn implicits on and off, but also to turn each specific implicit on and off.
My $.02 about implicit typing, as it seems to be biting many people…
We seem to have different interpretations of how the YAML type repository should be used. This ambiguity has leaked into the YAML implementations, and we need to clear things up.
[…]
The YAML type repository is NOT intended to be interpreted as “here is a set of types to use by default”. While _some_ of the types there certainly should be available by default, not all should. Instead, the repository should be interpreted as “here are some types, if you need them, use them in this way to maximize portability between applications”. This is why the types are not part of the spec itself - this set of types is dynamic and can grow without bound, without affecting parser implementations.
This punts the question of “what should be the default set of implicit types”? We have intentionally avoided defining one so far. […]
I swear I even found an email where one of the spec authors basically stated “[me and the other two authors] all use YAML for our own things things e.g. at work, and everything’s a string by default there too.”
I was not for the life of me able to find said email again, however. So take this as hearsay.
So, yeah. I really think the evidence can’t be argued with: the spec authors intended for YAML libraries to work with strings by default. It was their explicit recommendation. In fact, features like the notorious “no” were explicitly only added to the spec after this recommendation was the state of affairs.
So what the hell went wrong? Oh boy, that’s so easy to say in hindsight!
A critical lack of oversight
It is so incredibly simple for me, the future historian, to look at these emails and spot the spec author’s clear intent. For unclear reasons, this magic power was not bestowed upon anyone at the time. Some people were already writing YAML libraries before the radical typing change of October 2002, and many of those weren’t the spec authors. When implicit typing got completely overhauled? Nobody felt pressured to ensure libraries were up to expectation. Like, the Perl lib got fixed (spec author), but Ruby and Python never did.6
At least one library author, working on the Ruby library (Syck), never even got the memo about implicit typing being bad! Two of the emails up above, across the span of two whole years, were the same person still not knowing how their library should handle types in the following YAML:
true: y
false: n
x: 73
y: 129
But surely that’s not a big deal right? It’s one library author, what do they add up to?Receives call what’s that? Syck got accepted into Ruby’s standard library in August 2003? Oh. Ooohhhh.
That’s it. You’re done. You lost. Everything from there on out was inevitable. Like, god. The spec authors knew Syck was broken like this, but they didn’t think it was a big deal!
I think Syck has gotten a few things wrong, but we haven’t been sticklers because who wants to rain on Why’s parade. Still the new Yaml 1.1 implementations are getting things right and I expect Syck to follow their lead.
I’m struggling to even write this section because it just makes me wince. When do you think that quote was written? Hint: it’s after Syck shipped in Ruby’s standard library. So you might guess like 2003 still? Early 2004? No, you wish. This quote is from a June 2006 email. Almost three years after your “spec” started being consumed by a known-broken implementation shipping with a major programming language. Like, it’s obvious that the behavior in Syck is broken! They even spent hundreds of emails in 2002 debating the exact problem! But guess what?
They were replying to a user expecting the broken behavior. It doesn’t matter that the spec didn’t intend this. It doesn’t matter that experienced programmers like me would realize their library’s behavior was broken by design. YAML is what your YAML library loads, and to thousands of Ruby programmers, YAML loads no as a boolean, not a string. That’s it. You’re done. You lost.
I can only describe what happened here as a slow moving train wreck. The years of arguing, the complex spec language, the decisions to ensure implementation consistency, none of it mattered when the slow-moving locomotive of reality rolled by. It was so slow, they probably didn’t even flinch.
In the end
I’ve already stated what my original assumption was when I started researching: that YAML is fine, but you’re just using a shitty library. While I do think I’ve found sufficient basis to debunk many of the claims I’ve seen online, I also don’t think it’s fair to prance around victorious here. YAML is a spec, yes, but a spec is entirely meaningless without implementations. And the fact of the matter is that YAML drops the ball here.
In this post’s title, I used a very specific word. If I’m stalling for time finishing this article, I might at least try to answer the clickbait title question. Was somebody at fault? Can somebody be blamed? No, I don’t think so. But there is still something to be learned, at least.
For the sake of argument, let’s look at this from the perspective of lacking leadership. After all, you can always try to blame whoever is in charge7. They should’ve tried to communicate with library authors more. They probably should’ve written the “we recommend strings as default” into the spec. They could’ve been more bullish on expecting libraries be up-to-snuff. They could’ve demanded Syck be held back from Ruby’s standard library until the spec was v1. And so on.
But like, come on. YAML was not a thing that existed due to a corporate hype cycle and W3C backing. This was a couple passionate nerds doing their best on a mailing list. I didn’t dig into the portfolio of the 3 original spec authors for the purpose of this article; while they all seemed like proficient programmers in their own right, I doubt they had the necessary hindsight to oversee a project of this scale. The fact is that it’s an entirely different set of skills, and very few people people initiating these kinds of projects have them when they start.
And, of course, even if they had the skills, they may simply not have had the time and energy to spend on all of this. After all, the original scope for YAML was way larger than what ever came to fruition: stuff like YPATH, YSCHEMA, and so on never happened. Again, YAML wasn’t just trying to be a config format: it was trying to upend XML’s dream of large-scale data portability. And guess what? I could never blame somebody just for having massive ambitions.
Addendum: YAML 1.2
YAML 1.2, which came about 5 years after YAML 1.0, finally specified actual recommendations for what “schema” to use by default. They recommend a failsafe for “generic” tools, a JSON schema for, well, JSON compatibility, and a “core” schema with some reasonable extensions over JSON. No more “no”, no more base 60, etc.
Of course, again, it’s all down to implementations. And libraries like PyYAML, despite being officially maintained by the org, still don’t support it. This isn’t even a “you need to manually turn it on due to backwards compat”! They just don’t support it! Look, I doubt anybody’s getting paid enough for this, so I really don’t want to be too harsh on people, but this is just sad to watch.