Markdown Is a Terrible Language

🔗 **Source:** [Markdown Is a Terrible Language](https://www.youtube.com/watch?v=DWcqbPm_Rn4)
I've been a huge fan and advocate ofMarkdown for as long as I can remember,even to the point where it hurt mycareer. I literally didn't apply to jobsat companies where I couldn't submit myMarkdown resume as my official resumefor the company. I had my entire resumeon my GitHub, and if I had to or I wasreally interested, I would just submit aPDF with a single link to the Markdownfile on my GitHub. That’s how dedicatedI was. I just didn't think all of thesefancy ways of presenting informationmattered when you had a good, simple,reliable language for it specifically. I
will always be incredibly thankful toJohn Gruber and Aaron Swartz forcreating something as magical asMarkdown, having such a simple, reliableway to just write content and have itcome out and render so well. It stillhas a bit of a magical feel to it. Assomeone who tried really hard to makeLaTeX work in college, Markdown, once itclicked for me, just kind of became howI write everything to this day, which iswhy this article immediately grabbed myattention. Why the heck are we stillusing Markdown? As great as Markdown is,
I am more than happy to agree we havewent too far with it. Once we got to thepoint where we were making new fileextensions to embed other types ofcontent into our Markdown, we wereprobably over the line. And now thatwe're in a world where we're using LLMsand agents and all of these things, andMarkdown is the method we communicatebetween them, it's getting even morequestionable. I have a lot of differentthings I want to talk about withMarkdown in the near future, things likehow Markdown is not the right primitivefor memory or how Markdown is actually
pretty good as a programming scriptinglanguage in specific scenarios, whichwe'll definitely talk a lot about in thefuture. But today I want to question thefundamentals. Why are we still usingMarkdown? What makes it so great? AndWhat would a better solution for thestuff we do today look like? I've barelyeven started, and I can already imagineall of the Obsidian comments in my head,and if I know y'all love anything morethan Obsidian, it's probably today'ssponsor. I have a question for you.
What's the most times you've committedcode that didn't work just trying to fixCI? I've probably stacked up to 15commits before just watching CI breakagain and again. And all I wanted wasthe ability to run it locally withouthaving to make more commits, ideally ina way that doesn't even force me toleave my editor or my agent. Today'ssponsor RWX. I can tell you about howfast they are, how great they are towork with agents, or how they run thingsin parallel so you don't have to waitfor a bunch of stuff that's blockingwhen it doesn't have to be. There's a
lot of reasons to use RWX, but I don'twant to talk about any of that becauseI'm in love with the CLI. Once you writeThe YAML file for your CI, all you haveto do is run their CLI to trigger a runwithout having to commit the code. So,instead of committing, pushing, andwaiting for the whole thing to run, youcan just run a CLI command locally, itwill then spin up a cloud instance thatcan still take advantage of all of thecaching infra and all the other thingsthey do right, and immediately get yourfeedback as to whether or not it worked.
This should be enough to sell you. Butwhen you combine it with their agentskill, suddenly it's like, oh, yeah.
Seriously though, Run Loops are such agreat solution to so many differentproblems. If you were working on a hardfeature that you can't test, go writethe test, commit it, and then tell theagent, make sure CI passes by runningthe RWX run command, and then when itpasses, commit it and push. This is sucha good workflow, and I'm amazed nothingelse has it. And this is just the tip ofthe iceberg. The more you dig in, theThe more you'll appreciate RWX's unique wayof fixing CI so that you will movefaster instead of slower. Sign up forthe CI that makes your agents faster atswade.link/rwx.
So, why the heck are we still usingMarkdown? There are a few things in lifethat bring me much joy and heat at thesame time, like a chocolate that hurtswhen eaten, or Markdown. Seriously, why?
Half of the time we aren't even usingthe full language. Speaking of which,this is a bold opening. HTML is the bestprogramming language. I know you’veheard people say that the onlyprogramming language they know is HTML,and I know we both roll our eyes indiscontent trying to get our programminglanguage papers out of assembled deckson of papers on how HTML is only amarkup language and not a programminglanguage. I mean, yes, we're on theright, but that guy probably hassomething that we don't have.A life. Okay, already owned and theArticle's barely started. I will haveyou know that my girlfriend loves when Italk about HTML and Markdown.
But to each their own.Note, when the author is talking aboutMarkdown, he is specifically talkingabout Common Mark unless statedotherwise, because it is the unambiguoussyntax specification. I love theproject. I really appreciate theirefforts on making this language a bitmore grounded. It's not thespecification that's broken, it's thelanguage itself. Oh boy.
The good. Markdown is a minimal languageused for typesetting trivial documents.It needed to do one simple job, get aMarkdown file and output an HTML file.Its index is as legible as it gets, andit's easy to write even with no assists.Like the C language, you can see theoutput that will be created. Bold isalways open, closed B tags at the end,and the italics are the same. Learningcurve is simply nonexistent if you'reJust a casual user. Just one look at thecheat sheet and you're ready. But nowthe bad. We don't know what we want. Dowe want UI? Do we want a programminglanguage? We don't know. The only reasonfeature creep exists is because ofunclear specifications. You want a
minimal, easily legible markup language,you have Markdown. Simple as that,right? Well, here are two differentsyntaxes to generate the exact sameoutput. You have the single hash fortitle, hello, stars for doing an italicshere for I am, and then unambiguous,which you want bold, and then grammar ina block quote. This is roughly how Iwould write this. I would have useddouble stars for unambiguous, but I amwhat I am. Actually, actually, I wouldhave used double stars for unambiguousand underscores for that, so I guess I'mcloser to here. But I would never havedone the header syntax like this. God.
Yeah. There is almost no syntax overlapbetween these other than the block quote.
At the end. Woof. Oh boy, we're about tohave fun. Hope you have the two eyeballsenough to see that Markdown is not whatyou asked for. These produce twoidentical outputs, and this is just thetip of the iceberg. It has so many poordecisions baked in that if you try touse it, it will actively fight againstyou the moment you think you know whatyou're doing. Let's start with bold,italic, and bold italic. In Markdown,you can write a bold in different ways.
You have the double star syntax, doubleunderscores, or the B tag. And these arejust some of the ways a valid bold canbe written. And these are for CommonMark. If you're using something whichisn't marketing itself as Common Markcompliant, you can very well encountervalid stuff that produces the sameinput. Like this, or this, or yeah, God,yeah. We have had to deal with so much[\_\_] like this for T3 Chat, you guyshave no idea. It is so absurdlydifficult to handle all the differentWeird cases for Markdown rendering, butI will also personally say the edgecases for LaTeX are even worse. So,yeah.
>> [snorts]>> And don't even get him started onlayered ones like this. Oh God, I don'teven want to try to parse this. Okay, wegot the two star here, then this one isan italic. So, this whole section isbold here, this one is italicized. Thisis italicized.A is bolded here, not italicized, andthis is re-italicized. Of is italicized.This is italics, and this is doubled theitalics for the led here, I think. Thathurt.Or this. Oh.No. No, please no.I'm not even going to try on that one.This thing is actually so peak that wehave a class of parser vulnerabilitiescalled ReDoS, regular expression denialof service, that are affecting this.Like this 6.9 severity level CVE forMarkdown it. This is a legit DoSvulnerability.
You just put a bunch of stars in a linkand it can't process it. Beautiful.Beautiful.Yeah, this string here takes 65,553steps. That will [ \_\_ ] over anything.Great, super cool. We love that.So, how much worse can it possibly gethere? Exhibit B, ASM was a good idea,but this? In old languages wherecompilers were producing optimal codelike a river in a desert, inlineassembly helped them writeperformance-critical code with ease andthe cost of the compiler engineers'blood, sweat, tears, and the birth oftheir firstborn son. Yeah, writing yourinline assembly to get perfect code ishellish. It's one of the reasons that aproject like FFmpeg is so tough, but
also so important. Inline ASM allowedstuff like SIMD operations before thecompiler supported them. You want anoverview of early SIMD generationFailures, you can take a look at thosehere. Oh God, that's the last thing Iwant to look at. Now, let's take thiswonderful idea and bolt it directly intothe most bloated, single-threaded,sandboxed environment, expecting a simpleand easy way to write documents. Andthat's how inline HTML inside Markdownwas born. Oh boy. Inline HTML allows youto do stuff like this. I've had so manyproblems with inline HTML and Markdownpersonally that I am even more on thisauthor's side now.
Yeah. I am an insert simple programmerdoing span class fancy text elegantclose span programming. Div classanimation. And here is my portfolio.Close div. Isn't this just simple? Isn'tthis just neat? The main reason whycorrect Markdown parsing isexceptionally hard isn't becauseMarkdown syntax is so hard tocomprehend, it's only 1/10 of the issue.The real issue is that to ship aMarkdown parser, you also need to ship aFriendly HTML parser. If you're usingHTML inside of Markdown, why not useHTML from the start? Fair. I actuallyremember now, friend of the channel, who
might even be here right now, JacobEvans, tried really hard a while back,and it looks like he has given it upsince, but he tried super hard to have asingle Markdown file for his GitHubprofile and for his homepage by servingthe Markdown as HTML, because there'senough overlap that, hypothetically, youcould write a valid HTML homepage andhave it render as your profile as well,without code between the two. Literallythe same exact bytes for both. And theresults were hellish. He tried reallyhard to make it work.
But man, it was very fun watching himtry. Markdown in and of itself isn'tpowerful enough to satisfy the simplemonkey brain developers like the author,who's only satisfied when the site looksgood enough. Good enough, in this case,means it needs to have at least basic.
LaTeX support with TikZ support with theability to install packages, PlantUML,mermaid, custom styling, customshortcodes, tagging and taxonomy, properfootnotes, BibTeX support. I don't wanta simple job from the simple tool, too.To nail a painting to the wall, I need ahammer. In this case, the hammer ismarkdown. But what if I wanted to painttoo? I'll break the canvas the moment Itry to paint it with a hammer. Breakingthe canvas also means a whole lot ofCVEs, primarily around cross-sitescripting vulnerabilities. This has methinking. As we think, I want to take alook at my blog and see how [ \_\_ ] mymarkdown is. I haven't thought aboutthis in a while. So we already have ahelmet here. That's great.
I have an iframe embedded cuz it's aYouTube video. So in the first 13 linesin the second paragraph here, I'malready doing weird HTML [ \_\_ ] then therest is fine. Oh, here's a great one. Ineeded to have an image tag that wasAlso, a link. So I put an image HTML taginside of brackets to attach the link.
Isn't this wonderful?I had to check two posts to find some cursed [\_\_].More cursed HTML inlined. Man, I used to be so good at writing.It's a shame that the syntax is awful.Yeah. Get the idea. It —As soon as you want to use markdown for more interesting stuff,you end up writing some cursed stuff.
Every time we allow inline HTML, plugin hooks, or embedded execution engines,we expand the attack surface.The result is predictable.Recurring cross-site scripting vulnerabilities across major markdown implementations and much, much more.And this will continue to rise with the vibe of [\_\_] parsers that are hitting the market.
Yep. I've seen a lot of attempts to rethink how we parse markdown becauseall of the agents spit out markdown.We want to make things that are better, faster, more reliable,and all of them are just full of exploits, edge cases.
Vulnerabilities, and more. Apparently,Obsidian markdown is even more cursed.I'm sure I will love that. Speaking ofthings I love, we're now in exhibit C,the old and obscure syntax. Markdown,like all the good tech we have aroundthe World Wide Web, was made in the goodold 2000s. Markdown was inspired bypre-existing conventions for marking upplain text in email and Usenet posts.Mind you, before 2000, most serious maillooked like this. Do you see this quotesyntax putting the colon at the start ofevery line?
Oh god, this ending in the signature.Woo. Do you see the beauty of it? Thequote syntax, the vertical separationwith pipes. This is what markdown wasneeded for, which it would fail horriblyat because in markdown you can't dividethe screen into n parts without inlineHTML black magic. But because of thislegacy, you have two different ways ofwriting headings, the normal way and theATX way. You have two different ways andMore than two CVEs of writing bold anditalic. You have two different ways ofwriting horizontal rules, which collidewith one of them, colliding with the settext header syntax. You have twodifferent ways of writing unorderedlists. You have an ordered list, whichdoesn't care about how you ordered them.
And you have a footnote syntax, whichmoves the entire grammar tocontext-dependent grammar. And Iremember the first time I saw that, likethe right way to do lists in markdown,the correct thing is to put zerosbecause then your markdown parser cannumber them correctly, and if you changethe order of things, you don't have togo redo all the numbers. Ugh, so much[Â \_\_Â ] Oh, oh, I didn't see thisline. This language is literally the C++,of markup languages. Oh man. Now thatI've seen this, I can't unsee this. Thatis entirely correct. This might be myfavorite programming video ever otherthan maybe the OOP is garbage video.
It's 2 hours long and I had to stop andpause and go back to it later becauseunlike most 2-hour-long videos, Icouldn't watch that one in thebackground cuz it was too good. So Iactually sat and like locked in and juststared at my phone for 2 hours. Myroommate at the time asked if I wasokay. I was like, "I'm more than okay.You're going to have to watch thisafter." This video is so good. Highlyrecommend it. And knowing that, havingthat still in my head, C++ in markdown,yeah, there's a lot of overlap. Nearlyeverything can be done in two differentways, which some of them might allow forcross-site scripting and somehow leakmemory in HTML. It's used everywhere,but it also fails everywhere equally.
And now we're at exhibit D. You can'tsimply just parse it, you also have touse it. Parsing is the easy thing.If you remember, I told you that thefootnote syntax moves this grammar up toElucidate you. Okay, so if we have thissyntax with the carrot for a citation,this becomes test bracket one there. Butif we actually handle a citation at thebottom, now the markdown changes becausethis is now a link instead. Previously,it just put this in, now this thingfurther on changes how this part here isparsed. Oh boy. Actually though,footnotes aren't supported inCommonMark. They have links. The onlynotable difference is that link syntaxmeans you can put only one word afterthe definition as opposed to a shamefulexplanation of why bananas on pizza is agood idea. Reference style links andfootnotes require global definitionresolution. A token's meaning depends ondeclarations elsewhere in the document.
That breaks purely context-free parsingassumptions. Ergo, the updates to a CSGfrom a CFG. I had some intuition thatmarkdown as we use it was acontext-sensitive grammar, but goddamn,this is a really, really damning example.
Of that.Woo.All this a lot of words for saying, "Ifyou want a simple language, it shouldsay simple."Rendering is the hard thing. I thinkthis is the most controversial partabout the author's rant because nobodywants to admit that what we use and needare two very different things. Vanillamarkdown needs a transliterator, whichis literally a one-to-one mappingfunction. You see bold and then HTMLbold comes out. But modern markdown hasto support stuff like footnotes, which
takes this simple transliterator into afull-blown compiler. And after you madethis step, here's the ladder that you'llclimb. I need a personal knowledgemanagement system. Oh boy, the Obsidianpeople are going to love this.Requirement, I'll need footnotes. Cool.We're now at a CSG grammar. Now we'vbuilt a compiler. Oh, you want customcallouts? Hooks that bind HTML withMarkdown? Now you have a dependencygraph. I'll need math. Well, you needtypesetting library integrations, too.
So your dependency graph just got waymore complex with execution engines aswell. And if you want custom styling,custom CSS, you now have to deal withCSS injections with file-based scopingrules. This one is really fun. HavingCSS in one markdown file and making sureit doesn't break things in others, goodluck, have fun. This is a great section.I'm sure y'all will love. I swear what Ihave to do in Obsidian work to make myneeds using markdown make it seem likeNotion made a sane decision copying Wordand Excel internally instead.
Yeah. The author thinks that Notionseems sane because of how bad markdownis. Do you understand how absurd thatis? Honestly, okay, here I'm going todrop my hot take here. I like Notion'suse of markdown as hotkeys effectively,where you can export markdown fromNotion, but the formatting of it'sBroken and garbage. It's so bad. So like don't do that. But when I'm typing inNotion, the fact that I can just usemarkdown syntax to change the elementwithout having to learn a whole bunch ofnew hotkeys, that's really nice. Like ifI'm looking at the Notion doc for thisvideo, I say sup and I decide this shouldactually be a title. Hash space, nowit's a title. Oh, I want a smaller one,two hash space, three hash space.
That's really nice. But that's adifferent use case. That's not thesyntax we're using. That is a syntax ora grammar that we're familiar with beingused effectively as a hotkey layer. Fun.
Uh Wabo just linked me async syntax inMarkdown. This is JS though, but still,this syntax for doing execution withawaits in blockersis so cursed. This is like view levelsof cursed, but in a markup language.Async markdown,not even once.
This, this horrifies me.I feel sick. Oh god, there's more? Oh,this is DataView JS as text in. Oh, no, Iwant nothing to do with any of this.Save me. Get me away. No, let's see how we solve this according to theauthor. You ask the guy with a majestic beard with 30-plus years of experience in 8086 assembly, the answer is plain text.
You ask the guy with MacBook and OpenClaw running three different Mac minis making a startup a day, the answer is MDX.No, I okay, I'm not an OpenClaw guy with three Mac minis. I do have three Mac minis in this apartment, but only one is on.
I even know MDX is not the right answer here. You ask the guy who codes in C for a living, the answer is reStructuredText.You ask the author, the answer is none.All of these solutions are broken in their own ways. Plain text is beautiful, but I can't show it to somebody that doesn't know what a null pointer dereferences.
Wonderfully, if you only read it and neverwrite it. And MDX is so busy trying tobe HTML, it forgets it needs to belegible. And the biggest problem in allof them is that they don't have a buildsystem. We cut corners in the name ofspeed. If they had a build system, asane, unambiguous, legible syntaxpurpose-built for what it's needed tobe, I think all of the problems will befixed. Don't allow inline HTML, allowfor well-defined shortcodes andfunctions to manipulate the text. Allowfor custom hooks to be executed before,during, and after the compilation. Oh,so you are in favor of async markdown.And most importantly, define everything.What we need is a custom-built tool, nota Frankenstein's monster that we dare tocall a language. Not saying markdown ormarkup should be a programming language,I'm saying we are trying to use it likeone and because it lacks a formal
foundation, it's failing at both. Igenuinely think that a proper buildSystem around a saner markup languagewith compile-time hook support can fix alot of the problems around this with theright constraints. At this point, weshould just let go of markdown for goodand look for our answers elsewhere,probably with a trivially parsablegrammar. He has a whole formal languagetheory section here. Markup languagesare a standard text encoding systemconsisting of a set of symbols insertedin a text document to control itsstructure, formatting, or therelationship between its parts. Andprogramming languages are computerprogramming languages, any of variouslanguages for expressing a set ofdetailed instructions for digitalcomputer.
As you can clearly see, these officialdefinitions are not the way to go. Sohe's going to use another separate one.A language is a programming language ifit's Turing complete. Oh god, somarkdown is definitely a programmingLanguage. I write JavaScript for aliving, so we're not going to get intoEBNF and context-free languages.
Articles in the description if you'rethe type that cares about these depthsto Turing completeness things. God, noneof these words are in the Bible. Thething separating a set of mathematicalaxioms from provability tonon-provability is the want of recursion.Self-reference, which manifests itselfas the common multiplication operation.
A trivial observation can havenon-trivial reactions. Yeah. Oh God, Imissed this.Man, I forgot about this. This was in2012. Jeff Atwood wrote an articlebegging Gruber to give up Markdown or atleast bless these attempts to make abetter standard. Oh man, this isactually really good. I wish that thishappened. RIP. John Gruber's originalmarkdown.pl is one of the worst smallprograms I've ever read, completelyriddled with outright bugs andMisfeatures that continually bite itsusers in the ass. It's awful even by thealready low standards of handwrittenmany past regex-based spaghetti parsers.
Nobody should be using this originalscript, and unfortunately many of theother implementations out there aredirect transliterations that replicateall of its absurd errors. Like, where ifyou mention the MD5 hash of anothertoken in the document, the hash will bereplaced with the token because it usesthat as inline escaping mechanisms.Reddit hit with a cross-site scriptingvirus that got through the filtersbecause of this. Jesus Christ. Andanother Markdown is a disaster from lastyear.Wild. I did not know there was thisquiet cult of people fighting the chaosthat is Markdown, but it's really coolto see, and I have a lot of respect forthe author for taking the time to writethis. Thank you to Brock for writingthis. This was a fantastic read.
Not realize how much I hated Markdownand I use it every day. This articlevery much pushed me over the edge tomore deeply appreciate how bad it is.Sadly, this author does not have aTwitter or anything linked so I can'tthrow you at anything other than hisblog, but I would recommend checking itout if you're nerdy about these thingsand the original article has even morefun details if you want to check it outyourself. I got nothing else though.I can't help but notice the irony of mewearing this HTML shirt and I'm going toresist the urge to go rewrite all of mystuff in HTML or maybe a new markuplanguage I make up myself. Please don'tmake me do this, guys. T3 down is not thesolution, butI am admitting>> [sighs]>> A little bit of temptation.Until next time.Peace, nerds., Markdown Is a Terrible Language, YouTube, 01kq7dqdhpqg5w0d05efpnnx3g.