Anders Hejlsberg on Turbo Pascal, C#, TypeScript, and Programming Languages in the Age of AI

Open on YouTube ↗
Overview

Anders 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.

31 min read

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:

  1. A scanner (lexer) turns text into tokens.
  2. A parser checks token order and builds abstract syntax trees.
  3. A binder attaches symbol information to the trees. It finds declarations, builds symbol tables for later name lookup, and builds a control-flow graph.
  4. 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.
  5. 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.