Anders Hejlsberg on Turbo Pascal, C#, TypeScript, and Programming Languages in the Age of AI
The Pragmatic EngineerAnders Hejlsberg created Turbo Pascal, Delphi, C#, and TypeScript. In this conversation on The Pragmatic Engineer podcast, he traces that path from a high-school minicomputer in Copenhagen to the TypeScript team's current port to native code. One idea connects every stage of his account. A programming language is never just a language. It is an experience that includes the compiler, the editor, the debugger, and the tooling around them. Late in the conversation, he applies the same view to AI and asks which language properties help AI-assisted development and which parts of the programmer's craft will remain.
An HP 2100 with ferrite core memory
Hejlsberg started programming at a high school in Copenhagen that gave students access to a computer. He says it was one of the first high schools in Denmark to do so, in the mid-to-late 1970s. The machine was an HP 2100 with 32K of ferrite core memory, and you could open it up and see the cores. It had a paper tape reader. Later the school added a 1 MB, 14-inch hard drive, which he calls state of the art at the time. The machine had no ROM, so the bootloader lived on paper tape. At startup the machine knew nothing, and someone had to type in an instruction sequence that loaded the bootloader, which then loaded the operating system from the hard drive.
The machine offered Fortran, which he found quirky, and a very slow BASIC interpreter. It also had Hewlett-Packard's version of Algol. He remembers that compiler as odd because it did not support recursion. The machine's call instruction stored the return address in the first word of the subroutine, and a return jumped indirectly through that word. A function that called itself therefore overwrote its own return address and "you'd just be gone forever." With no debuggers available, you had to choose your algorithms carefully. Still, Algol compiled to machine code, and the students used it mostly to write games such as Lunar Lander. Looking back, he values how simple those systems were: "You could see all the way to the bottom." He says that stayed true through the 8-bit micros and early PCs, and that layers have been added ever since.
A Pascal compiler in a 12K ROM
Hejlsberg entered the Technical University of Denmark in 1979, just as 8-bit microprocessors were becoming available. They often came as kits you soldered yourself. The kits usually didn't work at first, and figuring out why taught you a lot about hardware. He bought a British Z80-based kit computer called the Nascom and learned assembly programming on it. With some college friends he founded a company that ran what he describes as the first computer store in Copenhagen where you could walk in and buy a computer. The store later sold Apple IIs, VIC-20s, Commodore 64s, and TRS-80s.
These machines all shipped with Microsoft's ROM BASIC. It was slow, and he missed having "a real programming language" like the Algol he had been taught. A co-founder pointed him to a new language called Pascal, which was supposed to be even simpler than Algol. Hejlsberg adds as an aside that this has been true of every language since, each one simpler than the last. Pascal was not hard to implement, so he wrote a compiler for a subset of it that fit in a 12K ROM. Users could pull out the Microsoft BASIC ROM and put in his. When they booted the machine, they landed in a small environment where they could type in Pascal programs and run them. He calls this an early precursor of Turbo Pascal.
Turbo Pascal: an experience, not just a compiler
The host suggested that Hejlsberg joined Borland and created Turbo Pascal there. Hejlsberg corrected the order. His Danish company eventually wrote a full Pascal implementation for 8-bit CP/M-80 machines. It then formed a joint venture with Borland, which he notes was also originally founded in Denmark, and Borland sold the compiler under a royalty contract. The first version of Turbo Pascal shipped in 1983. It "took off more than any of us had expected," and eventually became his full-time work.
He says the name was simple: "turbo" meant fast. It was the era of Audi's Quattros and turbos, and the product was fast and highly interactive. The design idea behind it went back to the ROM predecessor. You don't only compile programs. You also edit them, run them, debug them, and rely on a runtime library, and all of that has to fit together. The goal was to make a compiled language as interactive as interpreted BASIC while keeping the performance of compiled code and Pascal's better syntax and semantics.
He gives an example of how the compiler supported that cycle. Early versions had no debugger, so programmers used writeln statements. When a program crashed with a runtime error, Turbo Pascal printed the program counter address where the error occurred. The compiler had a mode that meant "compile, but stop at this address." Because the compiler produced object code directly and simply, it could stop when it reached that address. Whatever source it was parsing at that moment was roughly where the error had happened. That is how users jumped to the failing line, without line maps or a debugger.
He attributes the product's rapid spread to being better on every axis. It was faster, smaller, more interactive, and cheaper: "10 times better at a tenth of the price." Competing compilers cost around $500. They came without an editor and required swapping disks between compiler passes. Turbo Pascal cost $49.95, which he says was worth paying for the manuals alone. He believes the low price kept piracy low, and he recalls a running joke about the "Russian site license": Borland sold one copy to Russia, and it was copied everywhere.
Delphi: GUI apps, Visual Basic, and client-server
Hejlsberg says the big change between Turbo Pascal and Delphi was the move from DOS text mode to Windows GUIs, which required a new kind of application. Microsoft had shipped Visual Basic, which he calls very impressive. He says it still had weaknesses Borland knew how to compete against: it was interpreted rather than compiled, it was not extensible, and it lacked classes and object orientation.
Borland first set out to build a Visual Basic competitor, then decided that was not enough of an angle. At the time, client-server applications were growing, served by many 4GL tools for database-connected apps. Delphi aimed to be as interactive as Visual Basic for rapid application development, with a compiler behind it, and targeted at client-server enterprise applications. He says it worked well, and that many programmers still use it today. He praises the VCL (Visual Component Library) in particular. Developers could inherit from components, install their own on the palette, and use them with drag and drop in the forms designer.
The host added his own experience. He worked at Skype shortly after Microsoft acquired it, and the Skype application was built in Delphi. A plan around 2012–2013 to rewrite it on another technology stopped about a year in. The host guesses that Delphi stayed in the app until it was decommissioned.
Joining Microsoft: Java, J++, and the lawsuit
Hejlsberg joined Microsoft in 1996. The browser had arrived, and JavaScript with it, but he says nobody paid much attention to JavaScript then. It was slow, and people assumed nobody used it. Java drew the attention instead. It offered applets in the browser, a language that was simple yet object-oriented, bytecode, and platform independence. Many people, he recalls, believed Java would "flatten the universe" and that everyone would write Java from then on.
He came to Microsoft to be the architect of its Java development tools. He says Visual J++ 1.1, the version current when he arrived, was essentially Visual C++ with the C++ compiler swapped for a Java compiler. It was neither interactive nor built for rapid application development. His team built Visual J++ 6.0 around the interactive tooling knowledge he brought with him. They also built a class library for Windows desktop apps (he believes it was called WFC), which he describes as a precursor to WinForms.
Development went well, he says, until the Sun–Microsoft lawsuit. He stresses that the dispute was about business, not technology. Its effect was that companies would never bet on Visual J++, because nobody would write an app in a language that had been enjoined by a judge in San Jose. Microsoft concluded that it was not a good strategy to base its development platform on technology licensed from a competitor.
That lesson combined with a gap in Microsoft's developer offerings. Visual Basic was easy and well loved but had problems with performance and extensibility; new components had to be written in C++. C++ with MFC offered power and expressiveness. Developers wanted both in one product, plus modern features like Java's garbage collection, exception handling, and a more component-oriented model. He says these forces were the genesis of .NET and C#.
C#'s design goals
Asked whether .NET or C# came first, Hejlsberg says they were simultaneous. Microsoft wanted a language-independent runtime that could host Visual Basic, C++, and other languages. It also needed a new language that would appeal to both Visual Basic and C++ users, and, he adds frankly, one that could compete with Java.
He summarizes the overall aim as the power and productivity of C++ with the ease of use of Visual Basic. The concrete goals he lists are these:
- an object-oriented language;
- managed code, or bytecode, so it could target different runtime environments;
- garbage collection and exception handling;
- a unified object system, in which anything can be assigned to
object, value types are boxed, and every object is self-describing, so reflection can ask any object at runtime what it is and manipulate it dynamically; - first-class support for properties, methods, and events, because that was how components were built;
- standardization, handing the language to a standards committee to "level the playing field."
The host said C# was his first professional language, that he used it for about five years, and that he considers early C# ahead of some languages still in use today.
How the C# design group worked
Early on, Hejlsberg says, the team decided that a group, not one person, should design the language. He ran a group of six or seven people. They met three times a week for two hours each and started from the top. All of them had built or worked on languages before and knew what to do and what not to do. He says language design is "90% the same and 10% new" for almost every language. Every language still needs a compiler, compilers are still built in much the same way, and users now expect IDEs and frameworks as well. So much of the work draws on experience, and each round tries to fix the problems the designers have seen before.
He valued the continuity of the group, which worked together for years. Someone could arrive with an idea and immediately discuss it in depth with five or six people, without spending an hour first getting everyone on the same page. The group understood its job as trying to shoot down each new idea. An idea that survived that scrutiny was "probably a decent idea." Hejlsberg wrote the language specification alongside the design meetings. Another group implemented the compiler in parallel, in C++, or as he puts it "C plus minus," since it did not use all of C++'s features. The compiler was not written in C# itself until the later Roslyn project.
Tooling shaped the architecture. At the time, IDEs mostly offered syntax coloring, and statement completion was only beginning to appear. The team built a classic compiler plus a separate, corner-cutting "language service" that could do rudimentary completion and coloring. That meant two implementations to evolve together. As generics, LINQ, and other features arrived, every feature had to be implemented twice, which became a real drag. That led to Roslyn: a single compiler that works both as a command-line compiler and as an interactive service inside the IDE. He says TypeScript is built the same way, and that the lessons from this approach "are still not being taught in school."
For feedback, the team relied on internal customers. The .NET Framework team switched to C# early. They had been using what Hejlsberg calls a hacked-up version of C++. Other internal teams followed. The cycle was short. He says work started in late 1998, and by the 2000 PDC Microsoft was handing out beta copies and gaining large numbers of users.
Async/await and its trade-offs
The host asked why C#'s async/await design was copied by JavaScript, Python, Rust, and others. Hejlsberg explains the problem it solves. Many environments use cooperative multitasking: an event loop dispatches events on a single thread, and handlers yield back to it. Long-running work has to stop partway, yield to the loop, and resume when a result is ready. In that inverted architecture, the program has to become a state machine. People find state machines hard to write, because all state must move off the stack into objects and the logic ends up inside a large case statement.
He points out that the transformation from sequential code to this continuation-passing state machine can be done mechanically. With syntax that marks where to yield, the compiler can write the state machine. await is that marker: yield this promise here, and resume here when it completes. The compiler generates a large switch statement and moves any state that lives across the await onto the heap. The programmer gets "the illusion that you're just writing normal sequential code," instead of the callback style JavaScript suffered from.
He compares it with alternatives. OS threads bring preemption at arbitrary points, which is not always wanted, and they force multithreading into UI code. They are also heavyweight for small tasks. Async/await has its own cost, function coloring. There are now two kinds of functions, and async ones can call sync ones but not the reverse. Once one function becomes async, everything above it has to become async too. He cites Go's goroutines, language-emulated lightweight threads, as an approach that avoids coloring. For existing environments such as JavaScript, or C# on the Windows event loop, he considers async/await the right solution.
How JavaScript became unavoidable, and where TypeScript came from
Hejlsberg explains JavaScript's rise as several developments converging in the 2000s. Google's V8 made JavaScript reasonably fast. HTML5 was ratified, making real UIs possible. The iPhone set off a device revolution with many form factors, and all of them ran browsers with JavaScript. "The real cross-platform language isn't Java. It's JavaScript."
People began building ever larger JavaScript applications, outside Microsoft and inside it. One trigger was when the Outlook.com team asked the C# team to productize ScriptSharp, a cross-compiler from C# to JavaScript that treated JavaScript as an instruction set. Asked why anyone would want that, the Outlook team said it gave them a grown-up language with grown-up tooling: Visual Studio, projects, and so on. JavaScript, as they described it, was a scripting language with "shitty tooling." Hejlsberg's team concluded that "perhaps a better approach would be to fix JavaScript," since you would not become best of breed in the JavaScript ecosystem by telling people to write in another language. He acknowledges that CoffeeScript and other languages targeting JavaScript were popular at the time.
He considers JavaScript a decent small language and credits Brendan Eich with understanding functional programming and making functions first-class objects. What it lacked was a type system. The team knew from experience that without types you can build decent tooling, but not tooling that scales to large teams. There is no way to state intent in code, analyze it, or power completion, refactoring, go-to-definition, and find-all-references. From that came the idea of a superset of JavaScript that adds a type system that compiles away, with strong tooling built on top.
Open source inside Microsoft: from CodePlex to GitHub
TypeScript was open source from the start, which surprised many people given Microsoft's reputation at the time. Hejlsberg says the company was slowly realizing that open source was where developers wanted to be, while its "collective DNA" pulled the other way, and TypeScript sat in the middle of that tension. The team knew there was "absolutely zero chance" of winning over the JavaScript ecosystem with a proprietary language licensed from Microsoft. They got approval, he says, because two technical fellows, he and Steve Lucco, TypeScript's co-inventor, insisted on it.
He says they "paid some taxes." The project had to live on CodePlex, Microsoft's open source repository, "where exactly no one was," and for the first two years there was little response. Adoption picked up after the move to GitHub in 2014. That move also changed the workflow. Before it, the project was open source but not open development: the team published the code, then copied issues into an internal tracker. On GitHub, the entire workflow became open. He says he loves that way of working, that they have used it for over a decade, and that it is what made the product as good as it is.
Why TypeScript won: tooling, VS Code, and LSP
The host cited GitHub's November 2025 Octoverse report, which showed TypeScript becoming the most popular language on GitHub. Hejlsberg says this happened gradually. TypeScript first appeared around number ten and climbed until it sat next to JavaScript. Counted together, the two were already number one; the only difference was whether people wrote type annotations. Some early users relied on JSDoc types in comments, which TypeScript also supported, and over time more people concluded that types were the right approach. He attributes the adoption "absolutely" to better tooling: "adding an erasable type system and then using that to enable great tooling is really where the programmer productivity boost is realized."
He calls VS Code a sister project. It is written in TypeScript, was one of the earliest adopters, and the teams still work closely together. That relationship led to the Language Server Protocol, which he says nearly every tool vendor now uses for interactive IDE services. Oddly, TypeScript itself is only now switching to LSP, as part of the Go port, because it had its own precursor protocol from before LSP existed. He believes building the two projects in parallel in the open changed developers' view of Microsoft.
Inside the TypeScript compiler
Hejlsberg describes TypeScript's compiler as typical in some ways and unusual in others. The stages are:
- A scanner (lexer) turns text into tokens.
- A parser checks token order and builds abstract syntax trees.
- A binder attaches symbol information to the trees. It finds declarations, builds symbol tables for later name lookup, and builds a control-flow graph.
- The type checker, the largest part, verifies semantic correctness: it works out types, checks that they relate correctly, and checks that assignments and calls make sense.
- An optional emitter.
In most compilers the emitter is large because it produces machine code or bytecode. TypeScript's emitter did two jobs. It erased types, and it down-leveled newer ECMAScript features such as classes into constructor functions for runtimes that didn't yet support them. Down-leveling was popular early on. Now that browsers are evergreen, it matters less, and the emitter mostly strips annotations and produces declaration files that summarize modules.
The unusual part is the interactive mode the IDE uses. There, the compiler is a service analyzing a program that is "perpetually broken because you're typing." When a user presses dot, the compiler must work out the type of the expression before the dot, possibly resolving things in other files, within about 200 milliseconds or the IDE feels slow. That cannot mean recompiling 500,000 lines. In his example of 500 files, ASTs for the 499 untouched files are kept, and only the current file is re-parsed, which is roughly 500 times less work. Types are resolved only as far as needed to answer the current question. Everything is "lazy and deferred and functional and reusable." He says this differs greatly from how textbooks teach compiler construction.
What JavaScript lacks, and the freedom of gradual typing
TypeScript follows the ECMAScript committee and implements new features when they reach stage three or four. The team sees the type system as its own domain. Asked what he would add to JavaScript itself, Hejlsberg cites expression-oriented programming, which he likes from functional languages. In those languages everything is an expression, and let lets you name an intermediate result inside an expression, as in OCaml. In JavaScript, writing fluent chained code means stepping out to declare a variable whenever you need a name. He mentions the "do expressions" proposal but says it is taking a long time and may or may not happen. Overall he calls JavaScript a nice language with some issues that a type checker is good at catching.
He finds gradual typing the most interesting part of TypeScript. Types are optional, and they exist only for development and checking; at runtime they are gone. Because TypeScript does not generate machine code from types, it does not have to prove full correctness. In a structural type system with recursive types, some comparisons recurse forever, "staring into the recursive abyss." TypeScript can check a few levels deep, call that good enough, and move on. A compiler generating machine code could not do that without risking undefined behavior. JavaScript's runtime is already well defined, so "if we're checking 99% instead of 100%, well, heck, that's better than the 0% that JavaScript checked." He argues this allows language features other languages can't offer because they cannot reach 100%.
How the TypeScript team uses AI
Hejlsberg's daily work is on TypeScript, which has been moving to native code for about a year and a half. When the port began, AI was far less capable, so the team used little of it. He says they now use it "fairly well." AI reviews pull requests on GitHub; that started out not very good and is getting a lot better. AI also attempts simple issues and succeeds some of the time.
One use case comes from the port itself. The team snapshotted the source about a year and a half ago, so a backlog of pull requests to the old compiler must be carried over to the native one. AI helps move those, and he says that is going fairly well. It also handles drudgery, such as writing tests in the style of existing ones: "no one likes writing tests. AI loves writing tests." He is clear about the limits: "we're not at [the] point where it absolves us from understanding what we're doing."
Later he adds that AI has been of limited use for implementing the compiler and new language features. It is good at "surface-y stuff." It is not yet at the level of grasping how types, symbols, binding, and parsing relate, or choosing the most efficient data structure. He calls the project "one big brownfield" that new code must fit into. He also notes that training data contains few compilers compared with the huge number of React GUI apps, so AI's strengths there are no surprise.
Languages as the source of determinism
Hejlsberg argues that languages matter more with AI, not less. AI is stochastic by design. It can give a different answer to the same question because of randomness or a new model. Applications need deterministic behavior; he asks what a banking app would look like if it hallucinated. Programming languages are "where the rubber meets the road." The host noted that AI tools often write a Python program when asked a data question, and Hejlsberg agreed with the principle: don't ask AI for the answer, ask it to write a program that computes the answer.
Asked whether languages should be changed for AI, or new ones designed for it, he gives what he calls his flippant answer: the best language for AI is the one AI has seen most in training. That favors JavaScript, TypeScript, and Python, and the effect reinforces itself, though he says JavaScript and TypeScript are popular mainly because of the browser. He finds it telling that AI targets TypeScript rather than plain JavaScript and believes types help guide it toward better programs. He thinks TypeScript's mix is right: explicit types where there is no context, inference where there is. Forcing annotations everywhere would, in his view, make AI err more often, because it would have to track and repeat types. Inference supports "don't repeat yourself," and fewer tokens generally make AI more efficient.
Asked which language traits matter more in a world of machine-generated code, he first remarks that we may be "past peak truth" on the internet, with more low-quality content each day making training data harder to curate. He expects that to become a problem. On language traits, he adds locality to types and inference. Global state and files whose scope is unclear force AI to take in the whole program or spend large numbers of tokens on context. With explicit imports, a single file can be analyzed and its interface to the outside extracted without deeper knowledge. That shrinks the context needed and makes each module easier to summarize. The host recalled PHP's globals. Hejlsberg noted that early JavaScript had the same problem, with no modules and monkey-patching everywhere, and says ECMAScript modules are moving the ecosystem "towards sanity."
He also sees a gap in how agents navigate code. Agents rely on grep and awk, which are text search, not semantic search. Search for a common property name like count or address, and you can't be sure you are renaming the right one. LSP implementations already provide semantic answers, but they may need changes to be accessible to AI, since "AI likes command-line tools" and language servers are not command-line tools. He sketches one possibility: a server keeps a project "hot" and answers semantic questions from AI, then discards it after ten minutes of inactivity. He expects AI's ability to validate code semantically as it generates it to become increasingly important.
A changing craft: reviewers, architects, and responsibility
On how the work of software engineers is changing, Hejlsberg says that "in a sense, we're all turning into project managers," overseeing "an army of junior programmers called agents." The craft is moving from writing code to reviewing it, shaping architecture, and supervising work. He admits this takes away some of what he enjoys. For him the fulfilling part was always writing code and seeing it work, and he is less interested in reviewing. He thinks review could be made far more engaging. Today reviewers get diffs in alphabetical order and have to make sense of them alone. A more pedagogical presentation, with AI-generated commentary explaining the changes and guiding the reviewer, could keep the enjoyment in the work.
He calls it "foolish to think that AI will just eliminate programmers." Vibe coding is "wonderful as long as it works." When it goes off track, you don't know what's happening and can't get the AI to fix it, and "you can't absolve yourself from understanding what's going on." Responsibility also stays with the programmer: you can't tell the AI you're firing it. He sees AI as a productivity tool that will change how programs are written, since there is no point typing what AI can type a hundred times faster. When the host noted that the industry is unsure how much work will stay in the IDE versus move to agents and command-line interfaces, Hejlsberg said he doesn't think the steady state is visible yet, but he believes programmers will remain relevant.
Asked what he has learned about what developers want, he says productivity and "being in the zone": a tool that responds and feels like "an extension of my fingertips." A language designer must therefore look at the whole experience, which programmers spend most of their working lives inside. He thinks this explains why attachment to languages and tools can become "almost a religious thing." On IDEs and languages he says, "you can't have one without the other. Well, you can, but it's just not nearly as effective."
Performance, his setup, and a 30-year career
Asked whether efficiency still matters now that resources are plentiful, Hejlsberg says it depends on the kind of software. Performance is decisive for compilers and tooling, which is why his team is spending a year and a half on a native port. It is also decisive for cloud inference and high-frequency trading. Elsewhere, code is often fast enough that a tenfold slowdown would go unnoticed, and optimizing isn't worth it.
His setup is simple. He is "an old Windows guy" who works almost entirely on a Lenovo P1 laptop with a 15–16-inch OLED screen and no external monitor, because he likes staying portable. He uses VS Code and GitHub "all day, every day," and for AI mostly the GitHub and VS Code tooling, where he can choose the model.
He has been at Microsoft for 30 years and has worked on languages for about 40. Asked why he stayed, he says he loves developer tools. Programming languages pose algorithmically complex problems and have few dependencies, so you build from the bottom rather than "swear at" someone else's framework. He stresses that languages are a long game that runs in cycles of at least ten years. Version one has problems, version two fixes some, it is only at version three that a language "really starts to be great," and then people still have to be convinced to adopt it. Microsoft has let him put a large company's weight behind language work, an opportunity he says few places offer. He also values that Microsoft is fundamentally developer-focused: developers and enterprises pay the bills, not advertisers, which feels to him like "an honest day's work."
For a book recommendation, he names the one he always recommends: Niklaus Wirth's Algorithms + Data Structures = Programs, written in the 1970s and now available online. He says reading it was a revelation. It taught him hash tables and how to build a small compiler, and it is light on formal notation and rich in examples, which suited him as an engineer. He considers it still highly relevant, especially for programming languages, which he calls a well-established discipline that has been around for more than 50 years.
Why was it called Turbo Pascal?
Because it was fast. This was when Audi had their Quattros and their turbos, and this thing was fast and super interactive.
When you started out building C#, what were your design goals?
We knew we wanted to build an object-oriented language. We wanted managed code or bytecode so we could target different runtime environments. We wanted garbage collection and exception handling, but also things like a unified object system.
What do you think made TypeScript this popular? Absolutely because of the better tooling. We were totally right there that added an erasable type system and then using that to enable great tooling is where the productivity boost is realized.
What is your take on either modifying existing languages for AI usage or coming up with a language that is more suited for AI agents to use? Well, my flippant answer there is
Anders Hejlsberg is one of the biggest living legends in the tech industry. He created Turbo Pascal, Delphi, C#, and TypeScript. The impact he has had on programming languages and developer tools is immense. Today with Anders we discuss how C# might have not been born if it was not for the Sun versus Microsoft lawsuit over Java. The behind-the-scenes story of TypeScript and why open-sourcing it was a huge deal inside of Microsoft. What he's learned from 40 years of designing languages, including why IDEs and programming languages go hand-in-hand, and many more. If you want to be behind-the-scenes look at how three of the most used programming languages in history got built and how AI might change our usage of programming languages, this episode is for you.
Before we start, I'd like to introduce our presenting partner for the season, Antithesis. A definite trend that I'm seeing across the industry is a lot more focus on testing, unsurprisingly thanks to AI. We know that software is hard to test, and we also know that AI is making it worse. Thanks for producing increasingly more code and more complex code. The bottleneck is becoming reviewing the code, testing it, and trusting it. Or is it?
We tend to think that code reviews are the bottleneck because you cannot scale human reviewers with token spend. But the problem actually goes beyond code review. Really, it's about verification. We know that AI cannot verify itself. To verify the correctness of AI generated software, we would need to catch issues that traditional tests miss, including issues that we did not even think of in a code base that is changing at superhuman speed. Oh, and we need to do all of this before deploying to production.
The only way to verify that software works is to run it with realistic faults. And this is exactly what Antithesis does. You can bring the system you work on under test and verify that it works as it should. Teams at Etsy and Jane Street are doing just this. And I'm also starting to use Antithesis to test real-world systems. Over the season, I'll be sharing a lot more on how it works and how it can help verify that a system is bug-free. In the meantime, check out antithesis.com/pragmatic to learn more.
Anders, welcome to the podcast.
Thank you.
It's brilliant to meet you. You've created widely used programming languages including the one that I learned first to program with, Turbo Pascal. How did you get into programming?
Well, I was lucky enough to attend a high school that, this is back in Copenhagen, that offered students access to a computer. It was one of the first high schools in Denmark to do so. And we're talking, you know, mid to late '70s now. And I sort of got bitten by it then, you know, just this idea that you could program this machine and make it do things, you know, the wonder of figuring out how it was put together. Of course, it was like completely ancient by modern standards. It was like this HP 2100 with 32K of ferrite core memory. You could literally open it up and see the ferrite cores. I mean, it was amazing, you know, paper tape reader and, you know, and then we got a 1 MB 14-in hard drive and that was just state-of-the-art.
The bootloader was on paper tape cuz there was no ROM in the machine. So it started up and knew nothing and so you had to type in the instruction sequence to load the bootloader that would then load the OS off of the hard drive.
And as a kid, what did you start to program on it? What captured your imagination?
Well, this was a Hewlett-Packard, so it had Fortran, which I found to be very quirky. It had a very slow BASIC interpreter, but then it had Algol. Hewlett-Packard's version of Algol, which was an interesting compiler implementation because it didn't support recursion. Which is kind of bizarre. You know, the call instruction of that machine would store the return address in the first word of the subroutine and then just execute and then to return you would jump indirect through that word.
So, if you called yourself, you'd just be gone forever, you know? And of course, there were no debuggers or anything to help you figure this out, so you just had to be real careful about which algorithms you used, but it was compiled to machine code and it ran, you know, and it was like you could build games, which is what we mostly did, like Lunar Landers and what have you kind of thing, right? So, yeah, it was fun.
Back then, things were so simple, right? You could see all the way to the bottom. I mean, there was just no layering and nothing. You were right on top of the hardware.
I guess you needed to see all the way to the bottom as well, pretty much, right? Back then.
Well, I mean, it was just so simple that you could, right? And that was the beauty of those earlier. That was true with the 8-bit micros and even, you know, the early PCs and whatever, right? And then we've just added more and more layers over time.
All right, we have a lot of layers right now, that's for sure.
Mhm. And how did you go from building games to actually building your first ever compiler? And what was that compiler?
I started in '79 at the Danish Technical University. At that time also, you know, this was right when eight-bit micros were starting to become available.
Eight-bit microprocessors, right?
Yes, exactly. And there was usually these kits that you bought that you had to solder together yourself, and then of course they didn't work, and then you have to figure out why they didn't work. So, you learned a lot about the hardware, too. And I bought a British Z80-based kit computer called a Nascom. And started learning assembly programming on that one. And then I also met with some college buddies, and we ended up founding a company. We had the first computer store in Copenhagen where you could walk in and buy a computer, one of these kit computers. And later we sold Apple IIs and Vic-20s and Commodore 64s and blah blah blah, you know, all of those different ones, right? TRS-80s.
So, I did a lot of programming on those and found that programming was really the thing that I enjoyed. And of course, they all came with Microsoft's ROM BASIC, which was slow, but it allowed you to write programs. But I always like missed having a real programming language. Something like Algol or that I had been taught, right?
And then my buddy, the guy that I founded the company with, he was like, "Well, there's this new thing called Pascal. You want to check it out. And it's even supposed to be simpler than Algol," which was actually true of every language we have created. They got increasingly simpler as time went on. And Pascal was not that hard to implement. And so, I got interested in trying to do that. And then wrote a little compiler that fit into a 12K ROM that would compile a subset of Pascal. And you could then yank out the Microsoft ROM BASIC and stick in our ROM instead. And then when you booted your machine, you were in this little environment where you could type in Pascal programs and run them. And that was sort of the early precursor of Turbo Pascal, if you will.
Many years later, you joined Borland, and I think it was in 1989, and there you created Turbo Pascal, the programming language, but also the IDE, right?
Well, there's a little more to that story. In that company that we had back in Denmark, I ended up writing eventually a full implementation of Pascal for eight-bit CP/M-80. And then we ended up doing a joint venture with Borland, which was also a Danish company. It was originally founded in Denmark, and we made a royalty contract where they would sell our compiler on a royalty. And that's how I got involved in Borland. And we shipped that first product in '83, the first version of Turbo Pascal. And then that took off more than any of us had expected. And eventually that ended up being the thing that I did full-time.
Why was it called Turbo Pascal? I understand you added things on top of Pascal that was there.
Well, I mean, it was called Turbo Pascal cuz it was fast. Back then, turbo was like, it was Audi had their Quattros and their turbos and the whatever, you know, and turbo just meant fast, right? And this thing was fast. And super interactive, right? And so Turbo Pascal it was.
When Turbo Pascal became big, was it big just because of the compiler or also there was a dedicated IDE for Turbo Pascal, right?
Yes. Yes. That was always the idea, and that goes back even to the predecessor of Turbo Pascal, this idea that it's not just a compiler. It's an experience, right? I mean, you don't just compile your programs. You also edit them. You also run them. You also debug them. You also have a runtime library. It all has to like fit together. Do you know what I mean? And so Turbo Pascal was always about building that whole cycle and try to make it as interactive as BASIC was as an interpreted language, right? But giving you the performance of a compiled language and the better, you know, semantics and syntax of a Pascal versus BASIC. And so, that was sort of the idea from day one, you know. Focus on the whole cycle.
And so, when you were building the compiler, you were already thinking of ways that the IDE, for example, could make sense of or could have helpful features for, maybe, the editing or debugging, for example.
Oh, absolutely.
You know, the first versions of Turbo Pascal didn't have a debugger, you know. You would just use writeln statements and then you'd just see what happened, right? But, often if you had some error and it blew up, you know, with a runtime error, we would print out the address of the runtime error, which is where was the program counter at that point. And then we had a mode in the compiler where we would say, "Compile, but stop at this address." And so, the compiler was real simple. It would just produce object code. And then once it hit that address, it would just say, "Well, whatever I'm syntactically looking at right now, that must have been around where the error was." So, that was like how you could go to the line where the error had occurred. Do you know what I mean? It's not like we had like line maps or debuggers or any of that stuff. We just had the compiler and it was just easy to make it stop at a certain address, you know, in the object output and then show you where it was in the source code.
Why do you think Turbo Pascal was so popular? I remember back, again, this was my first programming experience. It was at schools. It was outside of schools for production software. And you said yourself that it spread like wildfire.
It was just better than all the competition. It was faster. It was smaller. It was more interactive and it was also cheaper. So, it was like 10 times better at a tenth of the price of the competition, right? Compilers back then used to cost $500 and they were just compilers and then you have to have an editor and blah blah blah and it was like this whole long-winded cycle of inserting different disks with compiler passes one and two and what have you, and here was this thing that just like made it all go away and you could get it for $49.95. And for $49.95, I mean, heck, that was worth it just to get the manuals that came with it, right? So, there was very little piracy because it was so cheap.
Although, speaking of piracy, we always had the joke about the Russian site license, how we sold one copy to Russia. And then that got copied everywhere.
But after Turbo Pascal, you built Delphi, which was an even bigger setup in many ways, that this was now an integrated environment for Windows development. How did you evolve ideas from Turbo Pascal and Delphi?
The big thing that happened there between Turbo Pascal and Delphi was the advent of the graphical user interface, right? We switched from running DOS in text mode to running Windows in a GUI and that meant a new kind of application, right? That you had to create. And at the same time, competitively, Microsoft had created Visual Basic, which was a very impressive product, but still had some of the very same flaws that we knew how to compete with, right? In terms of interpreted versus compiled and extensible versus not, or not extensible versus ours that had classes and object orientation and blah blah blah. First, we set out to build a Visual Basic competitor. But then we also realized that, well, that's not really enough of an angle. And then there was this other phenomenon that was happening at the time which was called client-server applications. And there were a whole bunch of 4GL application development tools for database connected client-server apps. And so, we set out to build a tool that was like as interactive and rapid application development as Visual Basic, but with a compiler behind it, targeted also at client-server enterprise apps. And that was what Delphi sort of was about, right?
It worked out really well. I mean, that product to this day is still being used actively by a whole number of programmers.
I was very surprised when I worked at Skype right after Microsoft bought it. I know. Yeah. The Skype application, you probably know this, it was built in Delphi. It was. And in 2012 or 2013, there was a plan to rewrite it and move it onto something else. That rewrite, midway a year in, stopped. So, I'm guessing that until the end of that Skype application, which was decommissioned maybe a year ago.
It's amazing, isn't it? I mean, Delphi was and is in some ways a wonderful way of building Windows desktop apps. I mean, they had a great, you know, the VCL, the Visual Class Library that allowed you to inherit components and install them on the palette and make drag and drop work for your forms designers with components that you had built and whatever. It was pretty cool.
Yeah, and we already heard the Microsoft link with Visual Basic. So, you joined Microsoft in 1996. You worked on J++ and then later C#, but can you take us back to that moment in time? What was the kind of programming environment like?
Well, the environment, particularly around the time where I joined Microsoft, the mid-90s, Java had happened. Well, the browser had happened first of all, and JavaScript. But JavaScript, no one really paid attention to JavaScript, cuz that was just this little whatever thingy that was in the browser, you know, and it was slow and it was like, "Eh, no one uses that." But then there was this Java thing that allowed you to create applets. Oh my god. Applets fantastic and right on the browser.
And everywhere. Yep, yep, right on the browser and everywhere supposedly, and this language that was like simple yet had object orientation and bytecodes and was platform independent and, I mean, it was like everyone was running around with their heads cut off thinking this was the end of languages. You know, Java's going to flatten the universe and we're all just going to be writing Java and Java applets and that's it.
Little do you know. And I actually came to Microsoft ostensibly to be the architect of Microsoft's Java development tools. And worked on Visual J++ 6.0 was the version that... They had Visual J++ 1.1 at the time I joined, which was basically take Visual C++, yank out the C++ compiler, stick in a Java compiler and call it good. But it wasn't interactive. It wasn't rapid application development and whatever, and I came sort of with a whole host of knowledge of how to build interactive development tools. And that's what we set out to do with Visual J++ 6.0.
And we also of course knew that, "Hey, you know, I mean people are going to be running on Windows and they're going to want to be able to build Windows desktop apps." And so we built a class library that allowed you to do that. This was the precursor... WFC I think it was called, but it was the precursor of WinForms, you know, in some ways.
How did the development of J++ go and eventually how did it lead to the idea of like, "Okay, let's do something else completely different?"
Well, you know, development of J++ went great until the big Sun Microsoft lawsuit got in the way. And there was, you know, and that is like, I mean, and now we're talking like business and whatever. It had nothing to do with technical, but it effectively meant that Visual J++ was never going to be a product that companies would make a bet on because they full well knew that, you know, you're not going to write your app in a language that has been enjoined by a judge in San Jose, you know, or whatever. And so, we kind of realized at that point, too, that maybe it's not a great strategy to place your development platform bet on technology that's licensed from a competitor.
And that in turn, along with the sort of dev situation at the time, I mean, Microsoft's main development products at the time were in two camps. There was Visual Basic, rapid application development, loved by everybody, you know, cuz it was so easy to build apps, right? But performance-wise, had problems, extensibility-wise, wasn't so great. To write new components, you had to write them in C++ and whatever. And then we had C++ with MFC and power and expressiveness, but really what people wanted was both. They wanted something that rolled both of those up, right? And then they also wanted like modern things like garbage collection that, say, Java had, for example, right? And exception handling and a more object-oriented, component-oriented way of building your apps, and all of that was part of the genesis that led to .NET and to the C# language.
So, which one was first, .NET or C#, inside of Microsoft?
Well, they were simultaneous, I would say, because we knew we wanted to build a runtime that was language independent because we knew that we wanted to run Visual Basic on it, and we wanted a way of running C++ on it, and we wanted the ability for other languages to host themselves on this runtime. But we also knew that we needed to build a language that would appeal to both Visual Basic and C++ users and give you sort of that golden thing in the middle, right? And to be frank, something that could compete with Java, right? And so that's why we started out building C#.
And then when you started out building C#, what were your design goals? You mentioned a few things with garbage collection or exception handling, but how did you come up with like, "Okay, what will this language be?"
Well, like I said, the overarching thing was this: power and productivity of C++ with the ease of use of Visual Basic, in a sense, right? But what it also meant was we knew we wanted to build an object-oriented language, we wanted managed code or bytecode so we could target different runtime environments, we wanted garbage collection and exception handling, but also things like a unified object system where, and that's true in C#, like anything can be assigned to an object, and if it's a value type, we box it, and it's a self-describing object. So, reflection, you can ask an object, "What are you?" and you can get all of the facts about it at runtime, and you can dynamically manipulate it in ways that just don't exist in a lot of other environments. So, we knew we wanted to go there with that.
We wanted a language that made this new model of properties, methods, and events first class, because that was how components were built, as opposed to just sort of functions and procedures and even objects, right? And then we actually also wanted to create a language that was standardized. We wanted to give this language to a standardization committee and try to, you know, level the playing field there. And all of those things were sort of like what was rolled up in C#.
You definitely did. C# was my first professional language where I worked for that I think for about 5 years, and I've seen both the tooling, the capabilities of language, and I still think to this day in many ways that old version of C# was ahead of some languages today in some ways. So, it's very interesting to see how rich that language was when it came out and of course the developer love that followed. But can you take us back of what did it take to build a language like this? Just, again, the software engineers listening, they're used to building SaaS apps, back-end services, you know, certain projects, but we are not familiar with what it takes to build a language, especially something with such large ambitions inside Microsoft. You knew millions of developers ideally would be using it. How did you get to this? How did you come up with the road map? How big or large or small a team needs to work on this?
I think early on we decided that we want to have a team of people design this language, not just one. I was sort of the guy who ran the group of designers, but we put together a group of six people or so, six, seven people, and we got in a room three times a week for 2 hours and just started the design, you know, like literally, let's start from the top. What is a... We all knew what a... I mean, these were all people who had built or worked on programming languages before, right? And had seen all of the things you're supposed to do and all the things you're not supposed to do.
And quite honestly, language design is 90% the same and 10% new for pretty much every language. Every language you build still has to have a compiler. Compiler is still built in pretty much the same way. And of course, as time marched on, people demand more and more. You have to have IDEs, you have to have frameworks, you have to blah blah blah, you know, and it's all there. So, there's a lot of experience you want to pull in. And there's a lot of work that you're doing that isn't really per se new. But every time around you try to fix the problems that you've been exposed to.
This language design group worked together for years on end. And it was lovely to come in to work with a new idea and then immediately have five or six people that you could sit down and have a deep discussion with without first having an hour of level setting. Do you know what I mean?
Yeah. Yeah.
And that worked really well because we could just jump right in, you know, and have two hours of technical discussion. And everyone was cognizant of, okay, if someone comes up with a new idea, now it's our job to try to shoot it down. What's wrong with this idea? Do you know what I mean? And if it could stand the test of that, then it was probably a decent idea. And so that was kind of how we ran the design.
And then I wrote the specification of the language in parallel with our design meetings. And then we had a group that was in parallel implementing the compiler. Actually implementing it in C++, or rather "C plus minus," because we didn't use all of the C++ features, you know, in that compiler implementation. But it wasn't until the Roslyn project that we self-hosted the C# compiler.
And Roslyn meaning that the compiler is in C#, right?
Exactly. Yes. That was a project that came later to build the compiler in itself. And also early on, you know, you've got to remember back then IDEs were not really all that fancy, you know. I mean, we had syntax colorization. Statement completion was kind of like, well, some IDEs were starting to dabble in it, but it wasn't really the norm. So, we built in a sense a classic compiler, but then we also built this like mini language service thing that sort of cut some corners and whatever, but could do some rudimentary statement completion and syntax coloring. But in a sense, we had two implementations that we had to evolve in parallel. And over time, that became quite a drag, right? Because as we added generics and added other features and LINQ and whatever, it was like, "Oh my god, now we've got to go implement all of these features twice, in the real compiler and in the language service," right?
And so, that ultimately led us to this project called Roslyn, where we built a single compiler that really is both. It's a compiler that can both function as a command line compiler and as an interactive service inside the IDE. TypeScript is built that same way, also. And there's a lot of learnings from doing it that way that are still not being taught in school.
There are many useful things not being taught in school, even when they are useful to learn about. One of these really useful tools that I must mention is our seasonal sponsor, turbopuffer. turbopuffer is a ridiculously scalable, fast, and cheap vector and full-text search engine built by an engineering team that I really like. The first time I heard about them was when I was talking with one of Cursor's co-founders about how their vector database could not keep up with the number of code bases that they were adding. This was back in 2023. Cursor did something seemingly risky. They took a bet on what was a little known and relatively new product at the time, turbopuffer. But it paid off. Cursor moved their semantic search workload over, and turbopuffer was indeed able to handle Cursor's massive, ever-increasing load.
The reason has everything to do with smart engineering. turbopuffer is built on top of object storage with smart caching on NVMe SSDs. Cursor's active code bases get loaded into the cache, so searches are fast, and inactive code bases fade into object storage. Cursor has so many good things to say about turbopuffer. They cut their semantic search costs by 95% when they switched. They think of turbopuffer engineers as an immediate extension of their team in Slack. And turbopuffer is one of the few pieces of infrastructure that they have not had to worry about as they scaled.
Today, turbopuffer indexes over 4 trillion documents for vector and full-text search and is used by the likes of Anthropic, Notion, Linear, Ramp, and many others. I'm getting to know the turbopuffer team and will share more about some of the cool things that they do behind the scenes throughout the season. If you need vector search or full-text search at scale, think turbopuffer. Check it out at turbopuffer.com/pragmatic.
When it comes to useful tools, I need to also mention our season sponsor WorkOS. One theme of today's episode with Anders is how he has spent the time frame thinking about developer productivity on a scale that most of us do not. Decades, not months or quarters. WorkOS takes the same kind of a long-term view on enterprise infrastructure. SSO, SCIM, RBAC, audit logs, they've spent years getting these right so you do not have to spend weeks implementing them. That's why the fastest-growing AI companies trust WorkOS. Visit workos.com to learn more. And with this, let's get back to building languages with Anders.
And when you're building a language, as your product was I guess the language itself, how did you get feedback? Of course, you already said the group criticized it. Did you have internal beta testers? Cuz again, for like a back-end service, you would typically have dogfooding, alpha testing, beta testing, and then you go public at some point, but this is not your average software as a service for sure.
Yeah, I mean, luckily, we had internal clients. The .NET Framework very quickly started implementing in C#. They had sort of used a hacked-up version of C++ to implement, which was kind of odd because, I mean, it was like targeting bytecodes, but not really. And so, they switched to C#. And that helped a lot. And then we had other internal teams using it and so we got a bunch of feedback that way, and then we had, you know, the cycle was not that long, right? I mean, I think we started in late '98 and by the PDC of 2000, we had... we signed up... I mean, we basically gave away beta copies, right? And got tons of users onto it.
Now C# introduced a lot of new features that were net new, I think, to programming languages. LINQ is certainly one of them, but one thing that might have been maybe one of the most influential parts that other languages adopted as inspiration was the async/await setup. Looking back, what do you think you got right with this design and why did it become so copyable across languages like JavaScript, Python, Rust, and others?
Well, a lot of languages are built around cooperative multitasking in the sense that they have an event loop that sits and dispatches events and then, you know, you handle the event and then you yield back to the event handler loop and it all runs in a single thread cooperatively, right? The problem with that is if you then want to do some long-running work, how do I stop in the middle of this piece of long-running work and yield back to the event loop cooperatively, right? And then when my result is ready, then I can come back and continue executing here.
Well, in order to do that in an inverted architecture like that, you have to build a state machine. And state machines are notoriously hard for people to implement cuz you've got to move all of your state off of the stack into objects. You've got to remember where, and then you have this big case statement that envelopes your entire logic and it's like a nightmare to figure out, right? But the transformation from serially executing code into a state machine, this continuation passing style translation, is actually one that you can do in a machine-based fashion. You can have the compiler write the state machine if you introduce syntax that allows you to indicate where you want to yield. And that's what await is.
Await is basically I'm saying I want to yield here. And I want to yield this promise and then when the promise completes I want you to come back here and continue executing. And then the compiler writes a state machine around it. And it actually turns it into this big switch statement, you know, and moves all of the state that survives across the await into something that's heap allocated so it can be brought back. And doing all of that work is something that compilers are great at.
And so that was sort of the idea that we have this new style of programming where we're using promises or the equivalent of promises and the ability to yield, and then we have callbacks, but trying to write your program in that style, that's also, you know, what JavaScript suffers from a lot, right? It's like all this callback style stuff. And with async/await you get sort of the illusion that you're just writing normal sequential code. And then the compiler does the painful transformation for you. And that turns out to be really useful.
Now, arguably an alternative way of doing this is to use threads in the OS. But the problem with threads is that they come with pre-emptiveness and the OS has the ability to pre-empt you at any point in time and that's not necessarily what you
want and your UI now you have to be multi-threaded in your UI and all sorts of other problems come along with it. Plus threads are heavyweight and typically not well suited for lightweight tasks like you could do with async functions. So there are pros and cons.
You know, like async/await introduces this notion of function coloring, which is unfortunate, where you have two kinds of functions, async functions and regular functions. And all the red functions can call the blue functions, but the blue functions can't call the red functions. And so that means once you want, you know, a red function, now everything above it has to be red. And so if you want from a sync function to call something async, well, then you got to turn this function into an async function and its caller has to be, etc. etc., right?
So that's unfortunate, and that's why some environments like Go, for example, has goroutines and green threads, which are really language-emulated lightweight threads that kind of do what I'm talking about, but at a much lower cost. But you avoid the function coloring. So there is a bunch of different things, but, you know, for an environment that already exists like JavaScript or like C# and the Windows event loop and whatever, this was the right solution.
Speaking of JavaScript, as C# was becoming really popular across startups, enterprises, and so on, it was exploding in popular games as well. To this day, it's very popular with games development. But JavaScript was starting to become more popular. Can you take us back to your observations on how JavaScript went from, you know, the mid-90s, this script language no one really took seriously, to just exploding in popularity?
I think it was sort of a confluence of a number of things that happened in the early 2000s, right? First of all, the JavaScript execution platform matured a lot. Like Google did their excellent work on V8, and all of a sudden made JavaScript a fairly performant programming language. HTML5 got ratified, and we were getting to a point now where you could actually build real UIs in JavaScript. And there was this device revolution that the iPhone set off. And all of a sudden we have all of these different form factors. It's not just Windows PCs on the desktop anymore. It's all sorts of diverse devices, but lo and behold, they all run browsers with JavaScript in it. Lo and behold, the real cross-platform language isn't Java. It's JavaScript.
[laughter] Who would have thought?
Exactly. And so the world started opening its eyes to that and started building larger and larger applications in JavaScript. And we saw that externally, but also internally. And one of the trigger events was when the outlook.com team came to see the C# team and asked us whether we would pretty please productize this thing called Script#. And we go, "Well, what is Script#?" It's this cross-compiler that allows you to cross-compile C# into JavaScript such that you can basically treat JavaScript as an instruction language and run your C# apps in a browser.
And I'm like, "Well, why would anyone want to do that?" Well, because then you can get a grown-up programming language with grown-up tooling. You can use Visual Studio. You can have projects. You can do all of these wonderful things, you know, that you can't do with JavaScript because JavaScript is just a scripting language with shitty tooling. And we were like, "Wow, really? Well, gosh, perhaps a better approach would be to fix JavaScript."
[laughter] I mean, surely you're not going to be best of breed in the JavaScript ecosystem by telling people to write in a different programming language.
Although, plenty of people were like, remember CoffeeScript and all of these other languages that targeted JavaScript, right?
Yes, so there were programming languages, right, which generated JavaScript, but it wasn't JavaScript itself. Yes, they were super popular. I mean, so many different things did that. But JavaScript is actually a pretty decent little language. There are just some things missing. You got to give credit there to Brendan Eich. I mean, he understood functional programming. And Brendan Eich, the creator of JavaScript, got functions as first-class objects right in JavaScript, which is a godsend and beautiful. But it doesn't have a type system.
And we knew from experience that you cannot build good tooling without a type system. You can build decent tooling, but it's never going to scale. It's never going to scale to large teams because you can't describe your intents in the code. There's no way of formalizing any of this stuff, and there's no way of analyzing it, and there's no way of using it in an IDE to give you statement completion and refactoring and go to definition and find all references and blah blah blah. All of that stuff, right?
That germinated the idea of, "Hey, we could create a superset of JavaScript that adds a type system, and then we could just compile it away." But now we have the foundation for great tooling, and then we could build great tooling on top and actually create a wonderful development experience, right? That was sort of what we set out to do.
When you set out to do this, you not only set out to do this, but you set out for some reason to do it as open source, which took everyone outside of Microsoft by surprise because old Microsoft under Steve Ballmer was notoriously perceived as anti-open source back then, with Windows and C# back in the day, of course, I'm talking about.
You know, Microsoft was slowly waking up to the fact that open source was not going to go away. And open source was where developers wanted to be, and they were voting with their feet. Yet, there's a collective DNA, you know, that has been trained to pull you in the other direction, right? And so, that battle, we were right in the center of that. And we full well knew that there was absolutely zero chance that we would appeal to the JavaScript ecosystem with a proprietary programming language licensed from Microsoft. No one was going to come. It had to be open source. There was just no two ways about it, right?
But getting that off the ground inside Microsoft, it took some pulling. And we paid some taxes. We did eventually get the okay to do open source because we had two technical fellows, myself and Steve Lucco, who was the other co-inventor of TypeScript, insisting that that was what we had to do. And so, okay, people weren't going to debate that. But of course you have to pay the tax and be on Microsoft's open source repository called CodePlex, where exactly no one was. And so, we were there for the first 2 years, and it kind of was crickets, you know. And it wasn't until 2014 when we moved on to GitHub that things really started to get moving with adoption.
And also, honestly, it totally changed our workflow. You know, there's open source and there's open development. And we were technically open source in the beginning, but it was not open development. We would sort of lob the source code out in this repository and scrape the issues off of that and put it into our internal issue tracker. But once we switched to GitHub, the entire workflow moved to open development also. And I love that workflow. And we've been there now for over a decade. And it's been fantastic. And it's what made the product as good as it is.
Just over a decade later, so the language moves to GitHub in 2014, and in November 2025 the GitHub Octoverse report revealed that TypeScript became the most popular language across GitHub. Outside of the type system, what do you think made TypeScript this popular? And of course, we've had other languages, Python being the other very popular one, but what captures developers' preferences this well?
Well, I think, you know, it didn't just happen overnight, you know? And if you look back, all of a sudden we surfaced as number 10, and then we climbed slowly over the years up and sat next to JavaScript, right? And of course, if you added JavaScript and TypeScript together, then we were already number one. It's just which syntax were you using, type annotations or not? And more and more people over time just decided to adopt that. I mean, some early on were using JSDoc or whatever, you know, like these types in comments, that we also supported. But gradually, I think people just realized, "Hey, this is the right way to do it."
And the reason they came, I think, is absolutely because of the better tooling. And I think we were totally right there that adding an erasable type system and then using that to enable great tooling is really where the programmer productivity boost is realized.
And I guess this is where we cannot not mention VS Code, which shipped that great tooling also as free to use, at least initially for most people, which also made a big difference.
Absolutely. Yeah, that's our sister project, which is written in TypeScript. And so, they were one of our earliest adopters, and we work pretty closely with them to this day. That whole interplay, in turn, is what led to the invention of LSP, the Language Server Protocol, that now pretty much every tool vendor uses to enable interactive services in the IDE. Oddly, it isn't until this port to Go now that we're switching to LSP. We had our own precursor of LSP because LSP didn't exist when we first integrated TypeScript into Visual Studio Code. But there are a lot of learnings from that. So it's been an incredibly symbiotic and fulfilling experience to build these two projects in parallel in open source. And I think it has totally changed people's view of Microsoft in the developer ecosystem.
Plus developers who again are not as familiar with compilers themselves. Of course, I use TypeScript and I'm aware that there's some compilation going on. Could you give us a brief overview of what the TypeScript compiler pipeline looks like in terms of parts, and what parts you specifically focus on more?
Sure. It's in many ways a fairly typical compiler and in many ways not. Pretty much every compiler has, you know, what's known as a lexer or a scanner that takes text and turns it into tokens. And then typically on top of that you have a parser that takes the tokens, checks their sequencing, and then makes abstract syntax trees, which is, you know, a tree that you can navigate that effectively is a map of the source code, but broken into syntactic primitives and checked that syntactically, or grammatically, everything is correct. So those are the first two stages of the pipeline.
Then we have one extra pass that we call a binder, which is, you know, once we have the parse trees, then we bind symbol information to them, where we find all of the declarations of variables and whatever and build symbol tables and attach them to their functions such that we can then later look up names effectively. And we also build in the binder a control flow graph, and I can talk about what that helps us do.
And then we have the type checker, which is the largest part of our pipeline. And that's the thing that checks semantically that your program is correct. It's the thing that figures out types and checks that the types relate correctly and that you're assigning the right thing to the right thing, and that, you know, you're calling something that actually exists, and so forth.
And then we have an optional stage at the end called our emitter. And normally the emitter infrastructure in a compiler is also quite big because that's where you go from intermediate representation to machine code or byte code. Now, in our case, we just erase types, if you will. Well, we kind of do two things in our compiler, actually. Early on, it was very much about A, erasing the types, but B, also down-leveling your code. So, we would take newer ECMAScript features that weren't yet supported by the runtimes, for example, classes, and then we would down-level them to constructor functions and whatever, and so we would rewrite the code. And that was a very popular feature early on.
Now, pretty much every browser is evergreen, and you know, ECMAScript features are caught up, and so that's not as important anymore. So, our emitter is effectively, you know, a thing that just erases type annotations and spits out the JavaScript code that can run unannotated, and also can spit out declaration files, which are summaries of your modules and so forth. But those are sort of the stages.
Now, the thing that's interesting about the compiler, though, is that it's built in a manner where it can function in a highly interactive mode, which is what the IDE uses. Normally, you know, command-line compilers, they just run through these stages, and, you know, the output is just whatever gets emitted or some error messages, right? But in an IDE, you know, the compiler is a service.
And what we do in that service is we basically take a program that is perpetually broken because you're typing. And yet we try to syntactically or semantically analyze it, because we need to know when you press dot here, what could come next? Well, that means we need to know what is the type of the thing you dotted on. In order to figure that out, we may have to resolve stuff. We may have to look at ASTs over here and whatever. And all of that has to happen within 200 milliseconds or else people think the IDE is slow.
Right? Well, what if you have 500,000 lines of code? You can't compile all of those in 200 milliseconds. So, you got to be super, super deferred and interactive. And so you got to do minimal amounts of work. And that's how our compiler is built: it tries to front-load. For example, you have 500,000 lines of code. Well, let's say in 500 files. Well, we could build the ASTs for 499 of the files and just sit on them. We don't have to rebuild those because you're not editing those files. We just have to update the AST of the current file you're in. So, that goes 500 times faster, right, than if we had to do all of it.
And then we don't actually have to figure out all of the types in here either. We can just start where you're at and then resolve just enough to answer the question that you're needing an answer for right now. And so everything is lazy and deferred and functional and reusable inside the compiler. And it's a very different way of writing compilers than what the textbooks will traditionally teach you.
Yeah, because I guess these are now interactive compilers, if you will, right? It sounds like it's more than a compiler, or a lot more difficult problem to solve.
The same engine is there, but you got to build it in a manner where it can be very interactive. And that was not typically important for compilers, you know.
And so TypeScript is a superset of JavaScript. What are some features you would try to add if only JavaScript would allow it, or if you were able to influence JavaScript's roadmap? What is something that you feel could make TypeScript a lot better, but of course there's a constraint there?
We track the ECMAScript committee, and, you know, new language features that get developed in ECMAScript, we implement once they reach stage three or four in the standardization committee. And we've sort of been on that train ever since the beginning. So, there is a pipeline that supplies new language features in a standardized manner. We sort of see it as our purview to define the type system on top. Right? So, that is, if you will, our playground. Now, I still have things that I wish I could have in the language itself. I mean, I like functional programming. I like functional
programming languages, and key to them is that everything is an expression. There's really no distinction between statements and expressions. And so, one of the features that JavaScript lacks, in my estimate, is the ability to give symbolic names to temporary results and expressions, and then reuse them. This is the let blah equals whatever in some expression, that functional programming languages, you know, like OCaml and whatever, all have.
And it's nice because you could just stay in an expression context, and you can just dot things together, and blah blah blah whatever, and sort of do this more fluent style of programming. But then all of a sudden you need a name for something you want to reuse, and now you've got to pop out and declare a variable, or turn it into state. Anyway, you know, that's one thing that I would like to fix. There's something called do expressions that may or may not happen at some point, but it's taking a long time. So, anyway.
But I mean, generally speaking, I think JavaScript is a nice little language. It just has some issues, you know, and I think we're very good at teasing them out with our type checker, right? And so, once you have a checker that can warn you, "Hey, you're about to do something stupid here." Then, it's not so bad.
The thing that makes it interesting, I think, and unlike pretty much any other programming language, is the gradual typing. This notion that you can have types, but you don't have to have types. Other languages force you to type everything, right? Because they in turn use that information to generate machine code, you know, based on what the type is, you know, different instructions for float versus int versus whatever, where in JavaScript, the types, or in TypeScript, the types are purely for the development experience and the checking. When the program runs, they're all gone. Now, of course, there are still types, but they're all dynamically computed.
But that's kind of interesting because that means in the language, we don't necessarily have to prove 100% correctness. And a lot of language features that we have, we can't 100% prove correctness. Like in a structural type system with recursive types, there are just cases that you can't analyze because the types are infinitely recurring. The more you try to relate two types, the deeper you go, and you're just staring into the recursive abyss. Do you know what I mean?
But you can kind of go, "Well, we've proven it to four levels. That's probably good enough. We're just going to say it's good enough." And then, you know, if everything else works out, we're going to go, "Sure." That you can't do if you were to go generate machine code that then would have indeterminate behavior, right? But JavaScript has a runtime where everything is well defined already. So, if we're checking 99% instead of 100%, well, heck, that's better than the 0% that JavaScript checked, right? And it gives you language features that no other languages can provide because they can't get to 100%.
It's interesting how constraints lead to innovation, or even limitations can lead to more innovation. Speaking of innovation, one of the biggest innovations that is everywhere is AI agents, AI coding tools that us software engineers, most software engineers, are using, increasingly using AI agents as well. As you're developing languages on a more, I guess, niche team, what kinds of AI tools are you using, or how is AI helping your language development work? May that be TypeScript or C#.
Day-to-day I work on TypeScript, and I can certainly talk about how we've been in the process of moving TypeScript to native code for the last year and a half or so. In the beginning of that project, AI was nowhere near as capable as it is now, and therefore we could not really use much of it in the beginning. At this point though, I'd say we're using AI fairly well. Obviously, we're on GitHub, we use AI to code review pull requests. That in the beginning was not all that great, but now it's actually getting a lot better.
We use AI to implement issues or fix issues, simple issues. And then it succeeds some of the time. In this port that we're doing, you know, because we snapped a copy of the source code from a year and a half ago and then ported it, we have a backlog of PRs that need to be moved on to the new native compiler, and so we're using AI to help us move those pull requests. And that's actually going fairly well at this point.
And then we use it for a bunch of drudgery work, like okay, here's this feature, please write me some tests in the same style as these other tests, right? And kaboom, it's like, no one likes writing tests. AI loves writing tests, and it'll just pump out more tests, and great, you know, so we're trying to use it to get rid of all the toil that otherwise we would spend our time on, right? But I would say we're not at a point where it absolves us from understanding what we're doing.
Not at all. No.
Well, plus your level at the stack, if you will, because you're building a language. You might argue that someone really needs to understand, at least one person. Ideally the whole team needs to understand those fundamental parts, right?
Oh, absolutely. I mean, language is interesting in the world of AI because AI, like this conversation, we wouldn't have this conversation if it wasn't for languages, because how would AI get to determinism without programming languages, right? I mean, AI is by design stochastic and indeterminate. It might give you a different answer the next time you ask it the same question, either just because random or because there's a new model or there's a whatever. There's no determinism. Yet we can't build applications if they have non-deterministic behavior. I mean, what would a banking app look like if it decided to hallucinate or whatever, right? So you have to have something where the rubber meets the road, and where you can reason about, and where you can replicate the behavior; every time you run the app, the same thing happens.
Absolutely. I mean, I even see it in almost all AI agents or tools these days: when you ask it something to do with data, oftentimes it will start writing a Python program, because I think the AI designers figured out that at some point you want to turn something non-deterministic into something deterministic. What is exactly the thing that we know is most efficient? You don't ask it for the answer, ask it to write a program that computes the answer. And you will know that that will be deterministic.
Yes, exactly. Yes. Yes.
It's very interesting. But speaking of languages for AI, I had a question that comes up, of course, because AI is everywhere and is generating a lot more code. What is your take on either modifying existing languages for AI usage based on what you're seeing with the patterns, or potentially coming up with... Would it make any sense to come up with a language that is more suited for AI agents to use?
Well, my flippant answer there is, you know, that the language that's most suited for AI is the language that AI has seen the most of in its training set, right? And that's why you could argue AI does really well on JavaScript and TypeScript and Python, because it's seen an awful lot of it, and there's an awful lot of that still, and that just reinforces itself, right? And you can argue, well, the reason TypeScript and JavaScript are popular, well, that's mostly to do with the browser and not so much to do with AI, right?
But it's interesting to look at why is AI targeting TypeScript versus just JavaScript? And there I think the types actually help guide the AI to producing better programs. And I think our combination of the ability to type something when there's no context, but also our ability to infer it when there is context, is just the right combination, because if you were to force AI to write a type annotation on everything, then it would probably get it wrong more often, because now it has to keep track of all these types, and it has to just repeat itself over and over and over, right? And so, types are important where there's no context. But inference is super important for the DRY, or do not repeat yourself, principle, right? And fewer tokens generally makes AI more efficient, and so I think we have a very nice combo there in how you can just sort of type the outermost parameter and then everything flows from there on in, right?
Now, one thing that AI is already resulting in, again, from the stats the GitHub team shares, so this is also open data, is that AI agents are generating a lot more code. I mean, they're both quick to generate, and they also like to be sometimes verbose. Knowing that we are already seeing a lot more code pushed everywhere, at a project level, at an aggregate level, what do you think language characteristics could become more important in this world of just a lot more code, oftentimes generated by machines?
I mean, you could argue that we're already past peak truth on the internet, right? And now there's just more and more garbage every day. It gets harder and harder to suss out the stuff that you do want to include in the training set in order to actually make something more intelligent. So I think that's that gradual... I mean, I'm sure people are working on it. I could see that as becoming problematic.
Languages that are suitable for AI, I think, like I talked about, types and inference. I think both of those are important. I think also locality is important.
Locality?
Well, what I mean by that is, don't have a bunch of global stuff where AI has to grok the entire product, or these pound-include files that are, oh my god, well, who knows where they're in scope, and how do I put that in the context window or not? And then do I burn a gazillion tokens on trying to include... But if you have good locality, where you're clearly stating what you're importing and whatever, and you can analyze just a single source file and from that extract its protocol to the outside world without having to know anything deeper. Do you know what I mean? I think those are important aspects, just simply to reduce the size of the context window and also make it easier to summarize each module in a program, right?
This is so fascinating because I remember, this was probably 15 years ago, where PHP was very much critiqued for its globals. And early on I didn't understand as a young developer why that was a big deal. I was just hacking on in PHP until I had the issue of something not working, and it turns out that something imported over with a global, and suddenly you realize that when things are defined across the code base, a global could be anywhere, and there's no way for you to know when someone else is doing... you're now back to the state problem, which you just talked about.
Original JavaScript suffered from this problem. There were no modules, right? Everything was global, and anyone could just monkey patch anything else, and it was impossible to know really what am I sitting on top of here? But now with ECMAScript modules and whatever, we're moving towards sanity, and more and more the world is written that way in the JavaScript ecosystem, and that's a good thing, and I think that will help us down the line with AI.
AI today is just starting to become aware of, you know, the existence of language services, and agents today like to use grep and awk and whatever, you know, to find all the places where you reference a certain thing, but it's not semantic search, right? And so if you have a common name for this property, like count or address or whatever, right? Well, it's going to find a whole bunch of properties named address, and then that's not going to work so well, because now you don't know that you're renaming the right one, right? But this is where language services come in, and semantic search. And I think that's going to increasingly become more important with AI. And really these are services that are already provided by LSP implementations. But they may need some tweaking in order for them to be more accessible to AI. AI likes command-line tools, you know, and they're not really command-line tools.
And I also wonder if, for example, performance will be interesting, because we know that these things can run faster, so faster feedback will clearly be helpful, which, we're now going back to... one of the reasons that TypeScript was so popular is the 200 milliseconds of getting you feedback, right?
But there are ways of, you know, where you can imagine, you know, a server keeping a project hot and giving you LSP services, you know, where AI can ask semantic questions and whatever. And then once AI stops asking for 10 minutes, the server just dumps it, you know, and whatever. So, there are ways of putting this together, I think, where we can make some progress, because the ability for AI to semantically validate the code that it's generating as it's generating it will increasingly become important.
What about the software craft? You've been in this industry for many decades, but it's hard to unsee that these tools are just coming to everyday use, similar to how at some point graphical IDEs came, and before that, I guess, higher-level languages came. Knowing that AI agents and AI tools will be part of the craft, what do you think parts of the software engineering craft will become less important, and what might become more important?
In a sense, we're all turning into project managers, right? And we can have an army of junior programmers called agents that will just spit out reams of code, but someone's got to have the big picture and review all of that. And so, increasingly our craft is going from one of writing the code to one of reviewing the code, and building the architecture of the code, and overseeing the work, if you will.
It's a different kind of craft. It's a different kind of enjoyment. I've always liked writing the code. You know, to me that was the fulfilling part, seeing it work. Do you know what I mean? And in a way AI robs a little bit of that, right? I mean, because I am less interested in reviewing code, but I think we could also make the process of reviewing code much more interesting than it is today, right? I mean, today you see a list of diffs in alphabetical order, and now it's up to you to make heads or tails of it. I mean, there are more pedagogical ways of presenting that, and you could have commentary generated by the AI that tells you what the changes are and whatever, and then tries to guide you along. Do you know what I mean? So that symbiotic relationship, I think we need to work on that more, sort of to keep the enjoyment in there.
But I think it's foolish to think that AI will just eliminate programmers, because ultimately, you know, vibe coding is wonderful as long as it works. And then the minute it goes off track, you have no idea what's going on, and you can't convince the AI to fix it. And so, what do you do? You can't absolve yourself from understanding what's going on. That's not programming. And ultimately also, you know, the responsibility for a program does not lie with the AI. It lies with the programmer. You're not going to go back to the AI and say, "Shame on you. I'm going to fire you."
What does that even mean, right? I mean, now you have nothing, you know. No, you need someone to have that function of being responsible. And so ultimately AI is a tool to enable us to become more productive, I think. But it will change the way that we write our programs, for sure. I mean, there's no point in sitting there and typing in stuff that AI could type 100 times
faster, you know. Having created three very widely used programming languages, what have you learned about developers, about what they care about when it comes to programming languages, and stuff that maybe they don't care too much about or don't even think about, but you might have to think a lot about?
You know, I think at the end of the day developers care about being productive. They care about being in the zone, where they feel like, "Oh, yeah, this thing is just clicking for me. It's doing just the right thing and it's like answering me." But it's right there. It's an extension of my fingertips, right? So, for me as a language designer, I'm never just looking at the language. You got to look at the whole picture. The whole experience. Because really what you're doing is you're creating an experience. An experience that programmers will spend the majority of their working life in.
Which is why programmers become so attached to their tools, you know, and their languages, right? I mean, it's almost a religious thing, which language you're on, which tool you're using, because it's so ingrained in your workflow and it so enables you to be in the zone, right? So, that I think is the key to focus on, and that's what I've tried to do with the work that I've done over the years.
And it sounds like this is why from the very beginning you also focused on the IDE, the tool where developers spend their time in.
Yeah, you can't have one without the other. Well, you can, but it's not nearly as effective.
Yeah, one question we're starting to see, or it's more of a question mark, is, well, how much are we going to be in the IDE all day versus these new interfaces, which might be agents where you can manage multiple things, or command line, which is again just something where we found that agents can work asynchronously. But I think we're still figuring out as an industry what will come next.
Yeah, I don't know that we can see the steady state at this point, because it's evolving so much, but I still believe that programmers are going to be relevant in this equation, you know. I fundamentally believe that.
What about performance and efficiency? Early on in your career, you just mentioned your first computer, how many kilobytes it had, and how you fit your compiler into 12 kilobytes, which these days I cannot even create a text file that's smaller than that, or it's very hard to do, right? But it seems a few decades ago, writing efficient programs was important, and over time my perception is that it's becoming less of a focus. What is your take on that, and do you think it's kind of fine for us developers to forget about efficiency, or we're just allowed to do that because we have more resources, or maybe this will change?
I think it's a case of it depends. There are certain classes of apps for which efficiency is absolutely key. I mean, the kind of program that my group works on, like compilers, tooling, and whatever. Yeah, people do care. That's why we're spending a year and a half moving to native code. Inference in the cloud, on the up against the line, I mean, oh my god, you know, like financial fast trading, whatever. It's all about perf, right? I mean, at the speed of light, and trying to move your trade faster than the other guys. So there are lots of places where perf is king. But there's increasingly also places where perf doesn't really matter because, you know, it's so fast anyway that even if it's 10 times slower, you still can't detect a difference. And so, it's just not worth optimizing there anymore. It depends, I think, on the kind of app you're building.
It's a good reminder that not all use cases are born equal. I'm interested, what is your personal development setup like these days?
Well, I'm an old Windows guy. Windows is still my desktop. I have a Lenovo P1. You know, I like just keeping everything portable. So, I don't have a big screen or whatever. This is like, you know, what, a 15, 16-inch laptop with a nice OLED screen and a nice keyboard. And that's what I do my coding on, you know, pretty much exclusively.
And what tools do you use?
Oh, I use VS Code. VS Code. VS Code all day, every day. VS Code and GitHub all the time. Yes. Yes. Yes.
And for AI coding assistants?
It's mostly the GitHub and VS Code stuff. So, which means, well, you get to choose your LLM there, right? But it's that workflow, I think, generally speaking. It's limited how much we've been able to use LLMs in implementing our compiler and implementing new language features. It's good at surface-y stuff. But when it comes to getting the big picture, and how do types and symbols and binding and parsing all relate, and what's the most efficient data structure here, and whatever, it's not quite to that level.
I'm also wondering if the lower you go into the stack, may that be very high-performance code or very concise code where all these things matter, maybe the applicability starts to drop. Because again, we already see this: LLMs are amazing for greenfield work. When you have an existing large application, it's useful, don't get me wrong, but it's not nearly as useful.
Yeah, and we are one big brownfield. Because, you know, we already have a huge code base, right? And it's got to fit in there. Plus, to be honest, there are only so many compilers in the training sets of AI, whereas there's a gazillion GUI apps written in React and whatever, right? So, no wonder it's good at those, right?
I was interested in reflecting a little bit on your career. You've now been at Microsoft for 30 years, and you've been working on programming languages for 40. That's even a lot to say, but in this industry, it's pretty common for people to change jobs every 3 to 5 years or so. What has kept you at a company for so long, and also in a similar area for even longer?
Well, there's just something about developer tools that is just what I love to do, you know? And programming languages, they're complex, algorithmically complex problems to solve, and I for some reason like that. They have fewer dependencies on other things, so you're building from the bottom yourself. Do you know what I mean? You don't have to sit on top of someone else's framework and swear at them when it doesn't do what you want it to do, right? So that kind of works for me, right?
But the thing is, doing programming languages, you come to realize it's a long play. I mean, if you look back at the stuff I worked on, it goes in 10-year cycles at least. And TypeScript didn't really, for example, or C# for that matter. I mean, it takes 10 years to get to... you know, version one is great, but it has all sorts of issues, and then you got to do version two, and then it's not until version three that it really starts to be great. But then now you got to convince people to actually adopt it, and it's just a long play. You got to be willing to do the long play.
And I think having been at a company like Microsoft, it's been great, because to be put in a position where a company like Microsoft is putting their might behind your efforts on creating a programming language, that's not an opportunity you get in a lot of places, right? And that has been fantastic. But also, you know, the fact that Microsoft is fundamentally a developer-focused company. And they always have been.
Yeah, that's how they started, right?
Developers matter. It's not advertisers who are paying the bills. It's developers and enterprises, you know, and I like that, where you feel like you're doing an honest day's work and people are paying you for it, and it's good stuff.
As closing, what is a book that you would recommend, and why?
I always recommend the same book, which is Niklaus Wirth's Programs plus Data Structures equals Algorithms. It's actually available online now. It was written in the '70s, but that was the book that... It was a revelation for me to read this book. This is how I learned about hash tables and how to construct a small compiler and whatever. And it was just wonderful. It's very light on symbolism and very rich on examples, and I was always an engineer, and so that book just appealed to me. And I think it's still in a lot of ways super relevant today.
The basics have not changed, have they? Too much.
No, no, and certainly, and particularly when it comes to programming languages, heck, it's a well-established discipline, quite honestly.
Yeah, it's been around for 50-plus years. Well, Anders, thank you so much for this in-depth conversation.
Oh, my pleasure. This was a lot of fun.
I hope you enjoyed this rare conversation with Anders as much as I did. An interesting part I keep thinking back to is how Anders said that programming language design is a 10-year cycle at minimum. Version one has issues, version two fixes them, version three is finally great, and then you have to convince people to actually adopt it. Most of us devs are used to thinking in quarters and sprints. This is certainly a different time frame.
I also found it surprising to hear how small the C# language design team was and how lean they worked. Six to seven people, three meetings per week, two hours each. All of them were people who had built languages before, and they were criticizing each other's ideas. And ideas that survived the criticism were the ones considered good enough to work. Which is a good reminder that standout technical work more often comes from small teams than it does from committees.
Finally, I really like how Anders said that IDEs are the language. From Turbo Pascal in the 1990s to TypeScript and VS Code today, Anders says that the compiler is not the product. The product is the whole edit, compile, run, debug cycle. This is a good reminder to any and all of us building software. The product is the complete way that your customers use the product, not just the screens or parts that you are responsible for.
If you'd like to go deeper on Microsoft's developer tool roots and operating systems, check out the related Pragmatic Engineer deep dives linked in the show notes below. If you've enjoyed this podcast, please do subscribe on your favorite podcast platform and on YouTube. A special thank you if you also leave a rating on the show. Thanks and see you in the next one.
Article published
