CoRecursive: Coding Stories - Tech Talk: Typescript with Chris Krycho
Episode Date: July 15, 2019Tech Talks are in-depth technical discussions. How do we make JavaScript easier to work with? Chris Krycho has been using typescript to add types to javascript since 2016. Chris is a software develope...r at LinkedIn who, at his previous gig, worked on converting one of the largest Ember apps in the world to TypeScript. I was shocked by the size. Chris also loves Rust and types and is a former C and FORTRAN programmers. He hosted a podcast called the New Rustacean, which he has retired from. Today we talk about TypeScript and when you should use it. We also talk about Language Server Protocols, Rust, working with large codebases, Structural types, row polymorphism and talking code over audio. Chris's Blog TypeScript New Rustacean Chris's Typescript Refinement types in TypeScript Winning Slowly Podcast https://corecursive.com/034-chris-krycho-typescript/
Transcript
Discussion (0)
TypeScript is the closest thing to dependently typed and refinement type systems that actually
is in production at scale anywhere.
It's not doing those things, but it's probably the biggest step in their direction that you're
seeing in sort of industry use on a wide scale.
Hello, this is Adam Gordon-Bell.
Join me as I learn about building software.
This is Code Recursive.
That was Chris Krejcho.
Chris is a proponent of TypeScript, who's been using it since 2016.
Today, he teaches me about what TypeScript is and why we should use it.
Chris is a software developer at LinkedIn who worked on converting one of the largest
Ember apps in the world to TypeScript.
I was actually shocked by the size of it. Chris also loves Rust and types and is a former C and Fortran programmer. He hosted a podcast called The New Rustacean,
which he's since retired, but was really well done. I hope you enjoyed the interview. And as
always, thanks to everyone who has tweeted or blogged or otherwise mentioned and join the podcast.
Chris, thanks for coming on the podcast. I mainly know you as like a disembodied voice that explains to me new features about Rust. But today, I'd like to talk to you about TypeScript.
I am glad to be here and looking forward to it. I will admit to have had the weird experience of
walking into a meetup room and saying to someone, oh yeah, hi, I'm Chris. And having heads turn because
they've heard my voice from Neuro Station. And let me tell you, that never stops being weird when
people just hear your voice and turn around because they recognize it. So, disembodied
voice on the internet is apparently my thing at this point.
Yeah. Podcasts are a strange kind of intimate medium, right? Because I feel like
from listening to your podcast that I know you, but you know, you don't actually know me.
Right. But I listened to your podcast, so I have the same feeling. It's random weird.
So what's TypeScript?
TypeScript is a typed superset.
Technically speaking, that's not true.
And I'll talk about that a little bit more in a minute.
But it's effectively just a typed superset of the JavaScript programming language.
And what that means is you are able to add type annotations to JavaScript.
And then you have a compiler which can do two modes, one of which is just check that code, and one of which is actually generate JavaScript or that can include various feature proposals and the like.
So a lot of places today will use TypeScript just for the type checking and then use Babel
to compile it so that everything's a single pipeline.
But the big idea for TypeScript is to say that huge amounts of the web at this point
and huge amounts of all of our infrastructure is increasingly written in JavaScript and in
large JavaScript applications. And many developers find that with sufficiently large,
sufficiently complicated applications, libraries, and so on, having a type system is a helpful tool.
So the original pitch, possibly still the pitch on the TypeScript website,
is JavaScript that scales. And the idea is you can gradually layer
in a layer of types on top of an existing JavaScript application or library, or you can
use them from the start of building a new one and start getting some of the benefits that types give
you. In particular, refactorability, because you can know rather than just guess heuristically where a given item is
actually being used throughout your code base. And a certain degree of reliability, for example,
things like null checking. You can tell TypeScript to, hey, check me and make sure that if this thing
can be null, I check it ahead of time. And that combination of giving you some much more
sophisticated checks on what you're doing, and then giving you tools for refactoring,
means that at least in principle, TypeScript code can be much more scalable JavaScript in
my experience. Now, there are a lot of my fellow JavaScript nerds out there who are saying,
ah, boo, you and your types. But for me,
the experience when I actually started pulling TypeScript into apps and add-ons libraries that
I was using was there were a bunch of places where I thought, oh, yes, I have exhaustively
checked all the ways that this could possibly ever be null. And the compiler said, you're wrong.
So the value proposition there is let's take this very dynamically typed language and give
you the tools to describe the types you're working with and in very rich, sophisticated
ways that do actually capture the incredible dynamicism that JavaScript has.
And that's probably what makes TypeScript particularly interesting from the perspective
of type theory or something like that is JavaScript is a wildly, incredibly dynamic language, which you can do ridiculous, crazy things with at runtime.
And TypeScript can actually represent in its type system, a very large and an ever increasing
percentage of that. I hope that's a good rough start to it, at least answering your question.
Yeah, totally.
So if I understand, TypeScript, like the short version, it's types for JavaScript.
So where did this come from?
Microsoft started the project in the early 2010s, 2012, 2013, I think.
And the lead contributor for it is Anders, I'm going to pronounce his
last name wrong. I'm so sorry. Helzberg? Helzberg? It's a name I've never actually
heard, just seen on the internet. But this is the guy who was behind most of C Sharp.
And before that was involved in, I think, Turbo Pascal or Delphi or both.
I think both, yeah.
Very, very important contributor to programming languages. And I like to joke that for good and
for ill occasionally, mostly for good, but occasionally for ill, TypeScript seems like
Anders' playground for interesting type theory ideas at scale. So came out of Microsoft,
continues to be driven by Microsoft and is heavily integrated
into Visual Studio and Visual Studio Code as a result. A lot of Microsoft's own open source
tooling is built with TypeScript at this point. I don't know his name either. Anders, let's say.
I like his taste. I remember, you know, I did Java development for quite a while,
maybe not quite a while, but then I switched to C Sharp and I was like, oh, it's like Java, but it's like, you know, it's kind of polished off. Yeah. They got some
nice things that I feel like he's very pragmatic maybe. And now he approaches a language like,
oh, we should have a way to do X. I think that's a very good description.
And that pragmatism is both one of the things that has made TypeScript very successful,
I think. And also one of the things that can drive me nuts on a day-to-day basis. Because, well, I like to call myself an idealist,
but we all know that everybody but the idealists likes to call the idealists impractical and so on.
I tend to prefer type systems that are a little more rigorous and a little stricter and more sound
in particular. The idea of soundness being that if you have a program that type checks is not
going to fail in ways that contradict those type checks. So examples that many listeners will be
familiar with are languages like Haskell or Rust or Elm. I believe Scala, though I would not quote myself on that, F sharp,
et cetera. And TypeScript is not that and TypeScript has become much more that,
but philosophically TypeScript is a deeply pragmatic language. And so the tooling
and the type system around it are very much built on enabling and empowering
working JavaScript developers, which is a goal I can really appreciate, especially as
someone who's worked in it day to day for a number of years now.
But there are definitely places where I want to be able to say, no, check me.
I don't trust myself.
I'm an idealist.
But what the idealism here means is that I'm a total cynic
about my own ability to actually get this right. So please check me exhaustively and rigorously
here, TypeScript. So is it not, this is a superset of JavaScript. So they're trying to
meet the JavaScript world where they're at? Exactly. And that notion of it being just a
typed superset with only one exception,
everything in TypeScript is just a type annotation, which you can strip out. And the only
exception to that is the enumerated type it supplies, which is just like your basic enum
from Java or C or whatever else, except that it can also have string values. But when you look at that, it just compiles straight into a simple JavaScript object with some key value lookup. So even that one
technically breaks the everything is just a type annotation that you can strip out.
It doesn't really break it. And so they really are just trying to say, we're not going to pull an Elm
or a ReasonML with BuckleScript or something like that,
where we're creating a whole new language for which JavaScript is the compile target.
Instead, they're saying, we're going to add types as a layer of very rich static analysis
and tooling integration via the language server on top of JavaScript. But it's still just JavaScript
with those type annotations in place.
So like practically, okay, like I have some simple website and it has a bunch of client
side JavaScript code.
And then I want to take that and turn it into TypeScript.
Like, what do I do?
You add TypeScript to your package.json and you type, you know, you run yarn or NPM install and you type TSC, which is
TypeScript compiler in it. And it generates a TypeScript config file for you in the root of
your project. You, depending on how you're building it, if you're using web pack or something,
there are starter packs for that. Ember.js has a tool that I helped maintain in the CLI that
just generates your configuration for you actually.
So you could, in Ember, you would just say Ember install Ember CLI TypeScript. I think there's a
similar just booted up experience for Vue and for Create React app where you can just say,
Vue CLI start with TypeScript or whatever. But in general, you just add this tsconfig.json file
and tell it if you have any weird layout things, you have to tell it about those. But in general,
if you're using a fairly standard layout for your file system, at that point, you're done.
You can just start type checking. If you want to integrate it into your build pipeline,
say you're compiling from JavaScript super fancy version next down to ES5 to run it on your build pipeline, say you're compiling from JavaScript super fancy
version next down to ES5 to run it on IE11, which, hi, that's sadly still the story for many of us.
Microsoft, please kill IE11. You'll use either TypeScript's own tooling to do that. The TS
config that it generates will by default target ES5 and therefore be suitable for running. I think it targets ES5. It'll actually go all the way back to ES3, which runs on
IE8, I think. Most people don't need that happily. You can either do it with TypeScript itself,
or you can just wire it into your Babel pipeline. And then you just start taking a file and saying,
instead of ending this file extension with.js, I'm going to end it with.ts,
just change the file extension.
And by default, that's enough
to start getting it type checked.
It also actually has a comment style pragma
that you can throw at the top of a JavaScript file
where you type a comment and then say,
ampersand ts check,
and it'll check the JavaScript file for you as well
in sort of its loosest mode.
So I'm not super, I don't know a lot of front end stuff. So I'm going to try to break this down
because like, okay, so Babel is some sort of translation layer from magic new JavaScript to
whatever I support. So let me take a step back. I live in this world every day and I forget that
it is not the world everyone lives in every day. Because we are targeting browsers in general, we have to deal with the
lowest common denominator of whatever browser we're targeting. And so while the JavaScript
spec continues to move forward and the language continues to move forward, unlike in a server
environment where we can control exactly what version of a given language runtime we're running
and can upgrade at our own pace. In the context of JavaScript, we may have to be targeting IE11.
And Internet Explorer 11 targets the ECMAScript 5 version of JavaScript, and it doesn't have any
features later than that. So although that came out the better part of a decade ago, and JavaScript has
changed enormously since then, whether that's the inclusion of things like classes as syntax sugar
for the function prototype way that you might have done things in the past, or the inclusion of
constant let bindings, which have local better scopes, unlike the old VAR in JavaScript, which
gets hoisted and has weird
scoping behavior that confuses people and breaks people all the time. Many new features like this,
generators, async await syntax, et cetera, can all be carefully compiled back to ES5.
If like me, you work at LinkedIn and we still support IE11. And so we need our rich client-side JavaScript to be able to run
on IE11, even though current versions of Chrome and Firefox and Safari and even Edge before it
shifted to being Chromium could all run stuff that's been part of the JavaScript spec even as
late as the last year or so. And so Babel gives us this layer of being able to say, I'm going to write modern JavaScript with all of these language niceties, and then compile it back
to an earlier version of JavaScript. So you'll often hear this referred to as transpiling,
because you're doing it not from targeting one language to another in the traditional sense,
or a lowering sense, but from one language to another equally
high-level language, which in this case just happens to be the same high-level language of
eight years ago or whatever it was when ES5 came out. So Babel will do that. TypeScript can do the
same thing and target an earlier version of JavaScript, or it can just target the latest
version of JavaScript if you're one of the lucky people who can get away with shipping JavaScript that only targets current versions of Chrome and Firefox and Safari.
So where does Babel run?
Babel will run as part of a build pipeline on your, whether that's locally or in a CI setup or whatever else.
So most modern JavaScript rich applications, in order to be able to take advantage of all this functionality that
we've built up, have a builds pipeline. And that's kind of weird in a lot of ways for people
who are used to the old include a jQuery script tag and be off to the races version of doing front
end web development. And for that reason, I'll frankly acknowledge that it can be a lot harder
to get started in some cases, and it can feel a lot more intimidating because it's a case of, wait, I have to run this builds tool so that I can do some
interactivity in my HTML page. And the answer is no, you don't actually have to do that.
You can still just drop a script tag on your page and be off to the races. But again, for those of
us building these rich applications, in most cases, we have command line tools integrated into that anyway, because we want things like linting to be able to say,
hey, here's some static analysis output, and to be able to run our test runner actively while
we're going and developing the app, or to be able to do boilerplate generation of, hey,
just give me a new React component or a new Vue component in my conventional locations for this app or things like that. And so...
And I assume, like at LinkedIn, they're not letting you FTP some JavaScript file.
No. And so we have a CI server where we're running all of that and running the build and running a
large exhaustive test suite, and then generating those target builds back in ES5. And a lot of modern build
tools can split it out and say, hey, we're going to generate a build for IE. And then we're going
to generate much smaller, more modern, faster builds that don't do all that transpilation
for quote unquote, evergreen browsers like Chrome and Firefox and Safari, so that you can, again,
get the benefits of an updated runtime that has support for these new features natively.
Because when you are back compiling, especially something complicated like generators or async
and await or things like that, you end up having to generate a lot of extra code to
support that.
And every app ends up having to make this trade-off of saying, do we use generators
here?
So for example, the app I work on at LinkedIn, when I'm working on this big app,
we don't use generators, because we have to ship this large runtime that basically acts like
generators are part of the language by shipping it as a library. But that's a lot of code.
And every byte you download and have to parse and then have to execute in the browser is a penalty
to the actual runtime performance of your application. So we don't use
generators in this app, even though they're really nice and make certain patterns really nice,
because we don't want to support that back to IE 11.
Makes sense. So a lot of people were already using a more modern version of JavaScript and
then transpiling it. TypeScript steps in and says like, well, what if you add some type annotations?
Then when you try to add seven to undefined, then we'll let you know. Right. Or you try to call a
function, it's undefined. And every JavaScript developer's least favorite, but most common error,
undefined is not a function. No. TypeScript says, hey, we can just layer on top of this existing
stack of tools that most of you are using and add this nice layer of static
analysis and tooling support. And when I say the tooling support, the language server that they
built around this is phenomenal. So whether you're using it in Vim or whether you're using it in
Visual Studio Code, you get this really great experience for inline documentation and code
completion and refactoring tooling,
all built on that language server. And it's a single language server, which Microsoft actually
introduced as part of building Visual Studio Code and TypeScript support and integration back in
probably 2013 or 2014, where they have this idea of instead of every editor having to rebuild its own tooling,
you can define a common layer and then a common protocol for talking to that so that every editor can have a language server protocol implementation and just know how to talk to
anything that speaks that protocol for any given language. And then you can have individual servers
per language. And then you can fit those two together so that if Sublime Text has a language server
implementation and Rust now has a language server implemented, well, those two can just
talk to each other basically for free without either of them having to do all the work of
re-implementing refactorings for Rust.
And then Vim gets that and Visual Studio Code gets that and so on.
So that came out of the work Microsoft did for the TypeScript language server
five years ago. And TypeScript remains kind of the canonical and best example of it.
I didn't know that. Yeah. I think the language server protocol, it's like a game changer.
So in my day to day, I'm using Scala. And traditionally, we always,
you know, everybody used IntelliJ because it was kind of the only really good implementation. And now there's a
language server protocol implementation for Scala called Metals. And just people are slowly drifting
off into using whatever editor they really want to use. Right. I mean, if I were IntelliJ, I might
be concerned. But I think that, yeah, it's definitely an enabling force for languages, right?
Like there's less overhead.
Right. Because that cost of finding a tool that knows enough about and can introspect your code
richly enough to do those kinds of refactorings and completions and all of that. And JetBrains'
tools, IntelliJ and all of those, have always done very well and been very good at that.
In my use of Rust, for example, I end up going back and forth because the IntelliJ Rust plugin and the VS Code plugin, each of them does certain things better than the other one or feels better than the other one.
I like VS Code better.
It's a little lighter weight feeling.
But the IntelliJ plugin is really good, and it's a little faster and more stable than the Rust language server. But that democratizing factor, I think you're exactly right,
ends up allowing new languages to be adopted much more quickly. So another example of that,
ReasonML is a syntax layer over OCaml that is primarily used for compiling to JavaScript via a OCaml to JavaScript compiler.
But they built a language server. And I think it's actually just a standard OCaml language server
that then can talk to VS Code or talk to Vim or whatever else. And it's the same thing. All of a
sudden, all of that capability just exists for anybody that has a language server plugin. And that's a huge thing
for somebody who wants to try using ReasonML, because the gap between that and whatever tool
they're already using is going to be so much lower than it would have previously.
Yeah. So what do I get if I move to TypeScript, the language server protocol, what does it enable?
So it enables refactoring.
So it'll say, hey, I want to rename this type, this class, or this variable throughout my
code base.
And you perform a rename and it does it.
It introspects your whole code base, finds everywhere that's used and does it with a
rename.
Or you say, I want to find everywhere this is used throughout my code base.
And whereas in a traditional JavaScript code base, you're left with
doing that as your kind of best guess in your sort of heuristic driven approach, which can be
very good. Or you're left with find all and grep and hoping that you didn't rename the wrong one
or hoping that you caught them all and hoping that your test suite is exhaustive enough.
If you have an application or a library that is actually exhaustively covered with types, the downside to what I mentioned earlier that it's gradual and
you can layer it in as you go is you only have to layer it in as much as you want to, which means
you can end up with a type system or a type coverage level that doesn't actually let you
get some of these benefits. But if you do have it fully and exhaustively checked,
and you don't use... I should come back to this in a minute. You don't use TypeScript's escape hatch called Any. If you're not using those things, then TypeScript can actually exhaustively
tell you, hey, here's everywhere this is being used, and you need to change it. And so you can
do the thing that you do in more thoroughly type checked languages traditionally of making a change
and then just following the compiler errors or doing a refactoring that can actually exhaustively
know where all of the refactor points are rather than that heuristical model or that
grep and hope model and trust your test suite.
I mentioned TypeScript's any.
I should elaborate on that.
One of the places where TypeScript leaves some unsoundness on the table is it has a type called any, which if you have a type that
is any, it can be assigned to anything and anything can be assigned to it. And this is
an escape hatch. Sometimes you're just in a spot where you're saying, I have no idea what this is,
or I don't have time to write the types for that corner of the ecosystem,
which leads me to another small tangent, which is to say TypeScript also gives you the ability to
write type definitions for third-party code. So you can create a description of what the types
are for some library that you're interacting with. So I'll come back to that in a minute as well.
But this AnyType, anywhere that you're using it, it's basically like, yeah,
this is just JavaScript, man. Do what you want. I'm not going to try to check anything anymore. And that's very powerful.
But unfortunately, it also just means that anywhere you use it, you're not getting any
benefits. And any type checking that happens to intersect with it, TypeScript's going to like,
nah, man, you said any. So anything goes here and you lose that. So in TypeScript 3,
they introduced a different
type called unknown, which is actually much more useful for the way I write JavaScript.
And unknown is a type that says, if you want to do anything with this, you have to check what
the type is. And that leads to the fact that TypeScript's type system is actually very
sophisticated and does flow analysis. So if you say I have an input
to my function and I can define that input as being an anonymous union type, or I can say this
is a string or a number, it's valid for you to pass me either of those as the argument to this
function. Then within it, you can write if type of this argument equals number within that if block, it now knows that it's a number and
it's legitimate to add it to other numbers and to divide by it and to do other things you would do
with numbers. And then you can put an else block and it'll know that, well, you checked in the if
block if it was a number. So this else block, it must be a string. And here you can call methods
that exist on strings to it, like to lowercase or to uppercase or
whatever else. And it won't let you do that in the number block. Well, if you take that and
include the idea of this unknown type where unknown, unlike any, won't let you assign it
to anything that is well typed. So if you say, let X be a number, and then you have Y, which is
unknown, and you try to assign Y to X,
TypeScript will say, no, that's out of bounds. You haven't actually checked that this thing Y
is a number. You can't assign that to X. So similarly, you can use this kind of
narrowing notion, which is what TypeScript calls it, because you have some set of possibilities,
and you're narrowing the set of possibilities. In the example I gave a moment ago from string
and number to just number or just string. Similarly, unknown says the set of possibilities. In the example I gave a moment ago from string and number to just number
or just string. Similarly, unknown says the set of possibilities is unbounded. I have no idea what
this could be, which sounds like any, but the difference is here, TypeScript checks you. It
says, no, you have to check. So if I have a function which takes in some argument, which is
unknown, and I say, if the type of this argument is a number, well, TypeScript now
narrows it. And it says in that block, hey, this is a number, you can do number things with it,
but you have to do that. And so unknown lets you deal with things like, hey, I got this blob of
data from across the internet. I have no idea what it is. I mean, in principle, it might be what my
API promised, but in principle, it might just be a bunch of binary garbage, or it might be a JSON object that looks nothing like what my API promised or whatever else.
And so unknown gives you this tool for describing that in a more robust way. Again, this is part of
that trajectory I described of TypeScript saying, how can we capture this thing that's really
useful, which we used to do in this more loosey goosey way, and give you a way to express
it with much more strict and sound guarantees. So today, if I'm converting a code base, there's an
ES lint rule, which is a JavaScript linter that integrates with TypeScript, where you can just say
never allow any. And if I were converting a TypeScript or code base to TypeScript today,
I would turn that on and say, it's legitimate for you to use unknown,
because then you'll have to check it, you'll have to do whatever runtime checking. And sometimes
you do have data that you don't trust or types that you don't trust coming in from the outside,
but then you're going to be safe when you're using it.
So one thing that you mentioned that I'm not clear on is like gradual types versus
type inference. So if I go back to your example, we take a JavaScript file, we rename it
dot TS. But so at that point, it has no type annotation. So correct. What are the types then?
Are they any? Some of them will be and some of them it will infer. So if you write let x equals
42, it's going to infer that x is a number. And if you say return X there from a function,
it will infer that the return type of the function
is also a number.
Now, if you have some conditional block
and you return 42 from one of them
and the string hello world from the other,
it'll happily just infer that the return type
from this could be a string or a number.
Cool.
And occasionally that's what you want,
but a lot of times it's not.
So often good practice is to then start adding annotations and say,
no, really, this function should only return a number. Please give me a typer if I return a
string. The things that will default to always making any are arguments to functions because
it does not do the kind of flow analysis and type checking analysis that some languages in the standard ML
tree of languages will do where it'll say, hey, I can see from how this is used that this argument
must be a number. It won't do that. So it will infer return types and assignments,
but it won't return function arguments. It also won't infer items declared in the body of a class. So if you
say class person and then declare a property in it name, but you don't give the name a type,
which is a valid thing to write in JavaScript, just name with a semicolon after it to say,
hey, there's a name property on this object. TypeScript will treat that as any as well.
And so at that point, then you can start layering in and adding in types
where needful to say, hey, no, this function doesn't actually accept anything. And there is
a flag in the compiler called no implicit any, which is part of that strictness checking,
which will say, hey, if you convert it a file from dot JS to dot TS, I'm going to give you an error
all the places where I could infer any and I'm going to say no you actually have to fill those in with a type so mostly function arguments or class properties like
that so that's the gradual typing side you can add in types progressively but then the other side of
it is you might want to represent types that exist outside your library for an untyped JavaScript code you're using.
So for example, Lodash is a very common utility library that many,
many JavaScript developers reach for. It's not written in TypeScript,
and it doesn't ship any types of its own as a result. But there's a repository called
Definitely Typed, which Microsoft maintains, where there are third party community definitions,
where you can just write out a function signature that says underscore dot map,
which is the low dash and underscore there is a function and it has this type signature and it doesn't have any body. So I'm just giving it a declaration. But I can say underscore dot map
takes in an array and a function to operate over that array.
And the function has to have the same type signature to work legitimately with this.
It understands generic types and things like that.
So you can write very sophisticated expressions that way.
And now when I interact with Lodash, I can also install the types from this library.
I can install them as at type slash Lodash as my package name for them.
And I'll just be able to say, hey, I actually get when I import and use underscore dot map,
I get type annotations for it. So if I misuse it, I'll get that feedback from the compiler,
and I'll get autocomplete from the language server and all of those things.
So you have this simultaneous ability to do type inference, which means there's
a lot of stuff you don't have to write. A lot of good TypeScript code, especially in apps,
tends to look a lot like the JavaScript code with little sprinklings of annotations. Like I said,
kind of at API boundaries is probably the best way to describe it at functions or class signatures,
and otherwise very similar to what you would already be writing.
In the body of a function, you're rarely going to write any type signatures because it does
have good enough inference for that. I mentioned earlier, I work with Ember.js and TypeScript a
lot, and we maintain these open source community definitions for Ember because Ember itself is
largely written in TypeScript at this point. But I mentioned earlier
semantic versioning and TypeScript not doing it. Ember.js is probably the most semantic versioning
thing in the entire world. It's very strict about backwards compatibility. So we've yet to define
a good story there between the two, because we really don't want it to be, yeah, Ember ships
its types now. And oh, you just upgraded from Ember 3.12
to 3.13 and your build broke because the TypeScript version that we shipped broke or
our type definitions broke. That wouldn't affect anything at runtime because it all gets stripped
out, but it's still a really bad experience for consumers of Ember. And since TypeScript 3.5
might break versus 3.4 in some small ways. We really appreciate that they're doing that work
because otherwise our lives would be much less pleasant in that space.
They're trying.
They are trying.
You mentioned earlier something that surprised me.
So my understanding is right, like TypeScript exists at compile time
and then at execution time, it's JavaScript.
But you mentioned looking at a type.
So if the type is X, then do Y. So how does that work? Because I assume at runtime, you just have JavaScript types. smart enough to understand that at time A, the type is string or number. But at point B in your
code, you've done this runtime checking. You had on someone who talked about refinement types in
the past. And I think you've talked to someone who had dependent types. And both of those can
do some of these kinds of things, expressing this idea of runtime behavior that has to be
validated by the actual checks you do in order to type check to some extent. And TypeScript isn't
doing either of those exactly. But through this runtime analysis, and also through some other
sophisticated things you can express with the type system where you can return different types
conditional based on the inputs
to the function. TypeScript is the closest thing to dependently typed and refinement type systems
that actually is in production at scale anywhere. It's not doing those things, but it's probably the
biggest step in their direction that you're seeing in sort of industry use on a wide scale.
And so it's using that notion of I'm going to,
and again, this is not the same thing as refinement types in Liquid Haskell. So please
don't email me, dear listeners. I know it's not that, but there's an analogy here where it's
taking the type and refining it using that runtime flow analysis. And it can do the same thing with
switches, switch statement where you have a set of cases within it and stuff like that. So you can actually emulate exhaustive pattern matching against a tagged union.
You have to do the work of building up the tagged union yourself because JavaScript doesn't have
those. And then you have to add in some machinery to say, assert that this thing will be never in
the default slot. Never is a type that if TypeScript sees that your runtime code ever actually hits it,
it says, hey, this is a type error. Again, through that kind of dynamic analysis of that flow down
through your code. So very sophisticated, very capable, so much so that in fact, even when I'm
working in stricter languages like Elm or Rust, sometimes I miss some of those incredibly rich,
very dynamic capabilities that come out of TypeScript as a result.
Yeah, it seems very flexible.
And I can see the connection to the refinement types because you're saying, you know, TypeScript is type annotations.
You know, in refinement types, kind of, at least Liquid Haskell lives in these like kind of comments, right?
Where you'd say like, hey, this int has to be greater than zero.
You know, if TypeScript were like, if you did like slash slash colon int, right? Where you'd say like, hey, this int has to be greater than zero. You know,
if TypeScript were like, if you did like slash slash colon int, right? If you put all the types in as comments, it would be kind of similar, right?
It's a similar space. Yeah. It's this extra layer on top of what the regular
story for the language is there.
And it's almost like, I mean, it seems to overlap with linting a bit too. Like is TypeScript a language? Is it a really fancy linter?
Right. That is an active debate in some of the communities I participate in. And actually,
as one of the maintainers of this Ember TypeScript integration, I make very clear to people that here
are what my preferences are. But at the end of the day, if you just want to use it as a really fancy linter, rather than doing what I described, borrowing the term from Edwin Brady, who is the
author of Idris and so on, this idea of type-driven development, where as much as possible, I'm using
the types to guide how I build. And I end up using a combination of types and tests, which can be
very, very powerful as a software, kind of one-two punch in engineering to
get rigor on both the runtime and the design space, both of them eliminating different classes
of errors. Well, look, at the end of the day, if you just want to write some pretty loose,
like loose Java style JavaScript, we're going to enable you to do that.
But over here is the happy path. Do it this
way, man, is kind of my verbal strategy in those conversations, as it were.
Yeah. And maybe you can gradually approach it.
Right.
Put it in your build pipeline as like a linter and then you slowly.
Yeah.
And I think that's one of the reasons that TypeScript has been successful,
because people can use it that way and then say, oh, I also learned this thing from
reading a little bit about Rust the other day. I wonder if I could do something like that over here.
Oh, I can. Cool. And that ability to gradually level up in your understanding of how to use
types is very powerful, I think. It doesn't feel like jumping into the deep end for a new
JavaScript developer the way that jumping into something like Elm or Rust can.
So you have done some sort of training on TypeScript, and I was looking through kind of your notes, and I found this quote, maybe you could explain. It says,
types are just shapes, exclamation. Maybe I need to say it with emphasis. Types are shapes.
Types are just shapes. Yeah. So this is one of the other really interesting defining features
of TypeScript. Most type systems that
most people are used to, including most notably in today's space, Java, C Sharp, and C++,
and for the most part, Swift, with an important qualification around protocols.
Certainly also Rust, Haskell, almost all of these have what we would call a nominal typing system.
And the idea there is that types are identified by their names. So if I have class person,
which has a name, which is a string on it, and then I have class human, which is a class,
which has a name, which is a string on it, those two are not substitutable for each other in any of the languages I just mentioned. In TypeScript, they are. Because
TypeScript looks at those and says, oh, that's a shape. And that shape is an object with a property
name that is a string on it. And the vast majority of the time in dealing with JavaScript, that is
exactly what you want. Because JavaScript is just dealing with
objects as these blobs of data. And you might use the name as a signal to other developers.
But for the most part, and this is a place where writing in TypeScript actually feels much closer
to writing in a dynamic programming language, a dynamically typed programming language,
than most others. It sounds like duct typing.
It sounds just like duct typing. Yeah. It's like duct typing that gets checked for you.
You say, hey, this function needs a thing, which is an object, which has a name on it, and it's a string. And TypeScript says, cool. Well, I have all of these objects in my system,
which have that. And some of them also have an age on it. And some of them
also have this walk method on them. But all of them are totally compatible with that because they match that
shape. And because they match that shape, they're good to go. And so a lot of especially functional
programming idioms, which often you really just want to say, hey, I only care about this one
aspect of this. And if you give me something with that aspect,
I can do something with it and then hand you back that thing transformed in some way.
TypeScript lets you do just that. It lets you say, hey, you hand me in an object with a name on it,
and I'll hand you back the length of that name. And I don't care how rich or sophisticated the
rest of that object is. I don't have to know anything about the details or the internals of that object. As long as you can't be an object that, among other things,
included a name that was a string, we're good to go. And that idea we call structural typing,
all you care about is the structure of the thing you're dealing with.
A couple other languages do have this. Swift's protocols are structural in nature,
which is very different for me.
I've been digging into Swift
over the last month or so, a bunch,
having spent the last almost four years
playing almost entirely with Rust in my spare time.
And Swift's protocols and Rust's traits look very similar.
There's this way to describe a behavior
that something can conform to,
like an interface in Java or C Sharp,
but which you can apply to something after the time of definition an interface in Java or C Sharp, but which you can
apply to something after the time of definition. So in Java or C Sharp, you have to say class foo
implements interface bar. In both Rust and Swift, you can say this type implements this trade or
protocol, whether Rust or Swift, well apart from the definition, which is very powerful. In Rust, you have to include the
body of whatever it is you're implementing. You have to define that there. Swift is perfectly
happy to say, oh, you already have the relevant properties and methods. Cool. You're good. So
you can have bodiless implementations. Also, Elm has this notion of structural record types,
where again, I don't care if your type is a
superset of this. As long as you hand me that, you're good to go. And I believe OCaml's notion
of row polymorphism fits into the same thing. But don't quote me on that because I haven't
done much OCaml. It's funny because I had an episode about PureScript, which I believe it
also has row polymorphism as one of its unique features. And I think it has something to do
with JavaScript, where if you're compiling something down to JavaScript,
you're like, I really want this.
That's a good insight.
And I hadn't thought of it before,
but I think that's exactly right.
I will also say that by and large,
a lot of the flame wars between dynamic and static typing
seem to me to be fairly well dissolved by this.
Not all of them.
You have some zealots on both sides who are like, well, we live in a universe where things are not actually perfectly computable.
And so types are a lie and they're always a lie. And you have people on the other side who say
stupid things like, if you're not using types, you're being unethical and irresponsible. And I
think both of those are really stupid things to say. And they're just, frankly, they're intellectually folly. Because it is true that you can't cover everything perfectly with types
on that side. But you also can't do that with tests. And most of the people who say that really
like tests, on the other hand, quite legitimate to say, look, for my use case here, I want maximum
flexibility. And I'm willing to take the trade-off that comes with that of losing some of
the things I might get out of types. So a couple of friends of mine will make exactly that argument.
And we have friendly back and forths about whether untyped or structurally typed is good,
or these nominally typed languages over here are good. And most of them would frankly admit that
if you're writing low-level system software that's a TLS implementation
or something, actually having Rust in its types is probably really high value. Whereas in other
systems, certain kinds of web systems where you're just doing really loose flowing data transfer,
something like Clojure or Elixir or Erlang, which doesn't have those, might actually be more
productive for you in certain ways. And so I think smart people recognize that there are trade-offs with these.
TypeScript and other things which embrace structural typing as at least one tool in
the toolbox seem to me to somewhat bend that curve and make it easier for you to say,
I'm mostly going to write this like it's structurally typed, just sprinkle in some
annotations on top and really be good to
go. And in the case of something like Elm, you're getting that with Henley Milner style type
analysis. So you're also getting this exhaustive whole program type checking with perfect inference.
And of course, you're going to add some annotations in general because they're handy
for other humans. But in my experience, structural typing
really is this incredibly sweet spot where it kind of feels like duct typed dynamically
programming, but you're getting all this help of the compiler saying, hey, this could be null.
And you might want to check that here before you just call the function so that you don't end up
with Bugsnag or Raygun or whatever saying, hey, undefined is not a function. You just had four
users who couldn't place orders because your function wasn't defined here. No, not that I have deep
and painful experience with this or anything. That's funny. Yeah. I remember I had an episode
with Jim Blandy talking about Rust. And I remember his big complaint was like, there's a million
line JavaScript code base out there. And if you make a typo, then you just have to wait for
somebody to hit that line and execute it. Yeah. It really stinks. And sometimes the way that you get to those combinations is just
incredibly arcane. There was a bug I dealt with in my previous role where we knew the bug existed
for two years and we could not reproduce it. We could never figure out the set of steps that
users were going through to hit that flow. And so we would see it show up with a stack trace in Raygun. And we would look at it and we would say, we would trace through every part of our code flow. And we would just say, oh, hey, this thing just happened. We were like, you found it. But it was a case of something ended up being possibly undefined.
And even when we had written the TypeScript, TypeScript has a couple of these little escape
patches. And one of them is you can write an exclamation point to say this thing will always
be defined. And we actually had a nice little comment on this block of code that says,
here's why this will always be defined. Spoilers, the comment was wrong. The compiler
was correct. Now, the problem wasn't that spot. The comment was actually correct in the small,
but in the large, the system was wrong. And it took two years. And finally, our product manager
just happened to be poking around one day and found the right combo that triggered it.
We fixed it and it went away and it was a good feeling. But that example
was one when I listened to that episode that I resonated with very, very deeply. I was going,
yes, Jim, that's exactly it. Yeah, that is a crazy story. Yeah. Whenever you can get something
out of comments and have it checked somehow. I remember years ago working on this old code base
and finding this method that I needed to interact with. I had this giant comment that said like, you know, this thing takes the following like
seven arguments and this one should be this and this one should be that.
And then looking at the method and it only took three arguments and they weren't the same.
Oh no, oh no. And I think that's one of the places where TypeScript
finds a niche is a lot of places, especially if you're writing large scale applications,
they're writing these big JavaScript doc comments that are annotating the parameters for the types already. And so you
actually end up with briefer, smaller amounts of code by adding the type annotations in place of
these JS dot comments. And then the compiler can check it for you. TypeScript also has a superpower
where it'll check your JS dot comments, which is really cool. It'll say, oh, hey, this is a valid block of JS doc. And you've described this parameter as being a string.
So now I'll actually check that if you use that pragma that I described earlier,
where you can say TS check, which is really nice.
Yeah, that's crazy. So Rust versus TypeScript, which is better?
Oh, man. So you remember how a minute ago I was saying that people say dumb
things on these type four arguments? For that reason, I just refused to answer the question
as stated because I think any answer I could give would end up being a dumb answer.
I like Rust better on a day-to-day using it level. For the last couple of months,
I've been working on a project called Volta, which we built out here at LinkedIn, but it's an open source project. It's a node version manager tool chain,
which gives you nice reproducible environments so that you can make sure that all the developers in
your team are using the same version of node and the same version of Yarn or NPM and the same
stack for any CLI tools they're using or things like that. And we wrote it in Rust in part because the original author behind it, Dave Herman,
helped drive Rust at Mozilla. And he's fantastic. If you ever get a chance to talk to him,
I look up to Dave a ton. But we've been using Rust. And so I went from my previous role where
I was writing TypeScript all day every day to my current role where I'm doing a bit of TypeScript, but mostly writing Rust every day.
And that soundness and exhaustiveness of it is just wonderful. And a lot of the built-in
language constructs that you end up having to build yourself in TypeScript, things like
rich enumerated types that Rust has, where you can say, not just this is A or B or C,
but A or B or C can themselves be rich data, not just an integer. So A can be A with a string wrapped inside it, and B can basically be a struct definition, which has all these fields on it and
so on. You can build up those kinds of abstractions and check against them in TypeScript, but you have
to do all the work yourself. And the language just guides you through that with Rust automatically and right out of the gate.
And it is deeply joyful to me. But I also have to admit that because of weird things about my
background, Rust just scratches an itch that's just right. Because the first two-thirds-ish of
my career up to this point, I was writing C and C++ and sometimes Fortran. And so I've spent so much
time in that kind of low-level world that Rust does this magic trick where I'm still writing
that kind of extremely high-performance, memory-controlled, low-level stuff. And I'm
also getting all these niceties of a high-level language. And so I like that enormously.
And the net of it is that Rust just feels better to me. But I can't say that either of them is a
better language. I think it's more accurate to say that both of them are very carefully,
thoughtfully designed languages that are targeting very different worlds and very different spaces and doing so very effectively.
Rust has this goal of zero cost abstractions and being able to be as fast and sometimes
amazingly faster than C or C++ and safe at the same time. And it's doing that incredibly well.
And there is a cost to it. I think the cost is a lot lower than some people sell it as being, but there is a cost to it. You have to think about things that you just
don't have to think about when you're writing C-sharp or JavaScript or any Python or anything
like this. And by the same token, it's not trying to be what TypeScript is trying to be.
TypeScript is, as you summarized it earlier, trying very hard to meet JavaScript developers where they are and enable them to, whether it's by way of a fancy linter or because
they actually just really are in love with Elm or PureScript or something, but for good reasons or
bad, can't write it in their job. And a lot of times it's good reasons. I mean, I love Elm,
but it was a running joke at my previous gig that I loved Elm and I would never
pitch it because it just didn't make sense for us in that context. And TypeScript made fabulous
sense. So whatever end of that spectrum you're on, TypeScript is meeting you there and enabling you
to do those things. And it just really doesn't have to try to worry about memory management
because it's not shooting for that space. It's funny. It makes sense that you mentioned
that you had kind of a C background
because in my mind, I was thinking,
it is strange for somebody to be interested
in both like Ember and Rust.
Like that seems like a very,
those circles don't intersect.
Yeah.
So like wrapping up on TypeScript,
when should people not use TypeScript?
That's a great question.
And I think there are a couple places
where I would say
don't. One is if you just try it and try it legitimately, not just this is new to me,
and I hate it, but legitimately go for it for a while. And it just doesn't pattern match right
in your brain, for lack of a better word. I have, I mentioned earlier, a number of friends
for whom even with structural typing systems,
as often as they try to use them, they just bounce off. These are people who know Haskell.
They're people who are really good, really top of their game engineers, and it just doesn't work.
At the end of the day, like I said, I think the using types is an ethical constraint thing is
just dumb. The best programming languages research we have says
you do find certain classes of bugs with types. That's great. It's helpful. But those kinds of
things are limited enough that we just don't really have any grounds for those kinds of
sweeping claims. And so if you and or your team bounce off of it, I think that's okay.
I think in certain cases, people's brains just work differently.
And I think that really is okay. My brain works in types. I've been trying to shove things like
pattern matching and those kind of rich data types into C since before I knew they existed
in other languages. I was trying to make this kind of thing work with enums and unions and whatnot.
Clearly, my brain just runs that way. I want to write these kinds of constraints. And other people don't. And
that legitimately is okay. If you have a team that is just hostile to it, don't try to force it.
It's not worth whatever gains you might get out of it. I would also say that in certain sufficiently
large code bases, if you don't have a lot of will,
it might not be worth it because you really do have to have the will to see the conversion
through and it's going to be long. It's going to be an enormous amount of human hours and effort.
Now I say this as someone who's been working on planning out how to do this on an app that has
over a million lines of JavaScript in it. So glutton for punishment, I don't know, something that way. But there are situations where
it may just not be worth the investment to you and your organization. And I think that's also
legitimate because these are engineering trade-offs. They're not things where we can
just snap our fingers and have the code base magically all typed.
If that were the case, I would say that almost everybody, unless you just can't do types, and even then, have you tried a system that has structural types?
You might try it.
It might be different.
But with that exception, if we could all just snap our fingers and have fully typed TypeScript things, I'd say just do it.
But most of us can't.
And there's an engineering tradeoff there.
And it may not be worth it.
It may not be worth whatever velocity it costs you in the meantime.
I think it'll probably pay for itself in the long term.
But I also think that gets at the other place.
If you're really just whipping up something that you intend to throw away, and you really
intend to throw it away, just do it in JavaScript if you're comfortable with that and you don't
care.
For me, I'm actually faster building those kinds of things with types. And I've also experienced that painful thing where you say, I'm just going
to build this little thing to wrap around and execute this as sort of my own mini test
configuration that nobody else will ever use, surely. So it doesn't matter if it's really
well factored. It just has to be a little script that I can get the job done with.
And then soon everybody in the organization is using that script. Things have a tendency to grow a life of their own. So you should probably be more skeptical of the idea
that you're really going to have a one-off throwaway script. But those kinds of things,
like I said, for me, I mean, I write some of those in Rust at this point. Call me crazy,
but I just, I like the feedback cycle. I get there and I enjoy using it and it works well for me.
So obviously I don't mind using types even for throwaway one-off
scripts, but that would be a place where people might not find the value trade-off high enough.
I just read an article that mentioned something about proof of concepts. And I agree with the
sentiment where it's like everything that's running in production was somebody's like
throwaway proof of concept, right? They were like, there's like the great vision that you
want to build and you never get to. And the proof of concept that still lives on, right?
Those are the two.
Yep.
And some of them are real nightmares, but they're still going.
And it turns out that code that's actually doing something is really valuable.
Delivering value, right?
So when I started this podcast, one of my ideas was sort of to get down kind of in the weeds of coding, like an audio format.
Turns out that that's like super hard.
So I don't know if I've given up on that.
But like your podcast, you actually have like code samples and walk through them.
Where did you come up with this style and structure?
I wish I had a good answer for that.
Mostly, I think it was that I was too ignorant of how incredibly hard it would be when I started.
And I thought,
surely one can do this, right? Maybe the reason no one has ever done this that's come across my
radar is I've just missed it. As far as I can tell, no. People just recognize that this is
really hard and you shouldn't do it. I ended up starting Neurostation when I did in part to help
myself keep learning. And then I found that teaching as that podcast was designed to do
without actually talking code sometimes is really difficult. Sometimes you can get away with talking
conceptually, but when you're trying to teach something like a programming language, you really
do just have to talk code sometimes because it's a programming language. So what I ended up doing
over the life of the show was figuring out how to boil down the
examples to really, really minimal things that you could actually maybe say out loud, and maybe
listeners could parse out loud. And then if they didn't, only have to re-listen to it once to catch
the parts that they missed. There was also an art I discovered over time of figuring out how you pronounce those
strings of characters. A function named foo, taking the arguments bar and baz of type string
and number and returning a new struct with these fields or something like that. Over time, learning
how to actually say it, because I was talking Rust and saying FN,
foo, open parentheses, which I did do in a couple episodes. And it turns out you just can't parse
that because you're trying to translate from audio into a visual representation of that.
Whereas if you just think about what the actual semantics are that that syntax maps to,
you can communicate that. You can say, here's a function named foo,
which takes bar being a string and baz being a number and returns a new structure,
which has some embedded field in it. I can say that and you can understand it.
Because at some level, when we read syntax, that's what our brains are actually doing.
We're not thinking fun when we see FN for
function. We're thinking, I'm seeing a function definition. And we get very accustomed to
parsing those details and translating them into the semantics they mean, at least at a high enough
level when we're reading through code. So the trick for me was finding a way to figure out how can I do
that parsing kind of ahead of time, do the pre-parsing to turn it instead of into some
bytecode or something, some AST, to turn it into something more like a syntax tree for English,
which is what we call a sentence in English. But to kind of do that mapping into what are
the actual semantics that we're thinking about. whether I would recommend it to anybody else. Well, I don't know. It was, it was really hard.
A lot of times I would end up leaving myself notes in the script of how to pronounce a given
definition or how to read a particular thing, because otherwise I would get to it while
recording and just say, Oh no, Oh no. And I would try it three times and have a whole bunch
of editing to do to try to clean it up into something that was actually useful.
Yeah. I think it's super tricky, but I think it's great that you made an effort at it. Maybe
we'll get better. Like maybe my rust is super rusty, if that makes any sense. Like I know
almost no rust besides talking to people, but I have explained to people my challenges with
the language by saying like, if you have a variable X equals one, and then like Y equals X, and then you print X, right?
Move.
Yep.
Yes.
That one sure confuses people at first.
So the punchline there is that will not compile.
And that may be confusing for you if you haven't worked with Rust before.
Though I think the specific example you just gave may compile because integers implement
copy. So by default, the compiler will just copy that for you rather than cloning it. But if you
did it with an own string, it would behave exactly the way you just described. So if you said let x
equals string from hello world, let y equals x, print x, boom, this doesn't compile. You moved
to y and you're going to say, what? What do you mean? What?
I've never seen anything like this. Yeah. See, this is why I don't do ad hoc reading a code in an interview. So I pulled this bio up of you. It says,
Chris is a husband and a dad, a theologian, a composer, a poet, an essayist, a software
developer, a runner, triathlete, podcaster, and all-around nerd. What's your secret? It sounds like you maybe consume a lot of amphetamines. Happily, no. I have a couple things going for me that way.
One is that most nights, I only need seven to seven and a half hours of sleep. And I'm not
one of those people that lies to myself about how much sleep I need. So I went through a pretty bad
season of burnout last year for a lot of complicated reasons,
including things like moving across the country after getting a master's degree and my dad having
cancer, brain cancer, which he came through well, he's doing really well. But that plus job stuff,
it just, I got burnout. And those days I was sleeping nine, nine and a half hours a night.
So I listened to my body very well. That is actually also part of it. I listened to my body
well, but I only need seven to seven and a half hours of sleep a night. And that extra time matters.
I work from home and don't have a commute. And for a lot of people, that's anywhere between one
and three hours a day. If you're riding a train, maybe you can get some of that back. But if you're
driving a car to get through LA somewhere, I mean, good luck. Maybe you're listening to an
audiobook at best.
So one thing I just always have to say to people when they ask this is I have a lot of extra time.
That adds up over the course of a year. Figure somebody takes four to six weeks off or whatever,
you're still talking about 800, 900, 1000 hours of time. So that makes an enormous difference.
And it's actually one
reason why I'm really, really bullish, I think is the right one on remote work and people being
able to work from home and not just in our industry, but wherever we can enable those
kinds of things. I also get to spend way more time with my wife and my daughters. And that's
massively important. I mean, I got to see them at lunch, I went upstairs and
made lunch for all of us and those kinds of things. So for those reasons, I don't work 10-hour days,
but I block out that somewhere in there, depending on the weather, whatever time of day is best.
I live in Colorado. So whatever time of day is best varies enormously. I'll be getting out for
a run or something like that. And I just know that there's going to be a couple hours in the
middle of the day where I stop and run and I go come back, take a shower, eat food,
et cetera. And then I hang out with my family in the evening. And then there's time after my kids
go to bed where some nights I'm just hanging out with my wife, but some nights I'm writing podcast
episodes or editing podcast episodes or whatever the case may be, working on reading some nerdy
theology book, so on and so forth. So it's mostly just having a good sense
of the rhythms of my day and sticking to those. And then like I said, all that extra time that I
get out of not having a commute and needing less sleep.
Yeah, I think it's harder than you're making it sound. So I work from home too.
The listeners can't see, but there's a chair behind me there. And I think that I have
dedicated some of the time you use for podcasting for sitting in that chair and
playing Angry Birds on my phone. I also got rid of Twitter recently. And I've been
really aggressive about removing things like social media. And that actually helps a ton too.
But I mean, I waste time sometimes. I also... This is huge. So I take a weekly day of rest.
And counterintuitively to a lot of people, I think that's actually
extremely helpful in this. So for over a decade now, Sundays, that's the day I pick because that's
when I'm going to church, it's time I spend with family, etc. I don't work on these things.
Sometimes I'll muck around with... This Sunday, I spent a little time mucking around with my
website, which I'm in the middle of redesigning. But I did that because it felt restful. And you read part of that bio, it included theologian.
I'm a Christian. And I actually, I look at this idea of Sabbath from the Old Testament,
the Hebrew Bible. And it just seems deeply wise to me. People sometimes get hung up on it as this
kind of binding rule. I actually just look at it very much as a gift. Humans need rest. And especially in our industry, it can be this weird badge of
honor to just say, no, I go all the time and I do stuff every day and I work 14 hour days.
And that's nonsense for one thing. You're not working good 14 hour days, seven days a week.
I just like to be effective with the time and the abilities I have,
such as they are, and with my absurd drive to always be writing a blog post. So I try to use
that well. I figure if I have it, I might as well bless others and encourage and help others with
it. But I also have to remind the people around me that just because I'm like this doesn't mean
it's more valuable or more worthwhile. It stresses the heck out of a lot of people I know
and love to think about trying to do all the things I do. And I say, no, it's okay. I just
really like doing things and I'm good at doing things. You don't have to be as interested in
doing things as I am. It's okay to spend more of your time proportional to me reading novels.
I spend a couple nights a week usually doing things that include things like reading novels or playing Mass Effect with my wife and then recording a podcast about it because
we're nerds and that's what we do. Check out MassEffection.com if you want to hear that.
It's nerdy and hilarious because it's basically us ranting about a video game and flirting. But
I do these things because I love them and I enjoy them and they seem to be an effective use of the
way I'm wired and built, but I don't feel the need to define myself in terms of them or find my worth in doing them.
So much of our sense of fatigue comes out of this notion of deep obligation that comes out of that
sense of measuring ourselves and our worth out of our productivity, rather than these being things
that we can do freely and enjoy. In my
view, because we're made to be creative in the image of a God who is creative and made a pretty
spectacularly crazy, weird world out there as I look out my window right here. It's a wacky place.
Have you noticed? It's wild. And so my worth doesn't hang on that. So it really is okay.
Well, that's a very healthy attitude. And I have to think that some of your success relates to that. Like the fact that you explored rust,
not as an obligation, but as a joyful endeavor or whatever you want to call it.
Yeah. I think that's right.
Well, I think we went through all my questions, Chris. Where should people
find you online if they want to learn more about you?
As I mentioned, no longer on Twitter. The only thing you'll find on my Twitter account is a link to all these other places. My website is the main place, chriscrycher.com. I
blog a lot there, as I mentioned. You'll find updates there multiple times a month, sometimes
multiple times a week, rarely multiple times a day. I try not to do that because it usually
means I'm not doing something else I should, but occasionally let myself get away with it. I also have a couple newsletters, one at button down dot email slash Chris Kreicho, where I'm doing some long term thinking out loud about questions around culture and late modern individualist liberalism and kind of these big structures societally. And a lot of cases,
just trying to funnel people toward people much smarter than me and much better read than me who
are saying interesting things about them that I think are worth engaging with. Sometimes that I
disagree with, but that I think are worth engaging and thinking well on. I also have a newsletter for
a side project I'm working on at button.email slash rewrite, where I'm trying to build the
world's best research writing application, which is probably the most absurdly ambitious thing I've
ever done in my life. And I have no idea whether it'll succeed, but hey, it'll be fun while I try.
And perhaps most interestingly, and related to some of these notes, we've just kind of touched
on, I have another podcast called Winning Slowly, which you can find at winningslowly.org, where a friend of mine who's a professor at Arizona State University and I try to talk through what it looks like from to talk about literally the entirety of human existence because technology, religion, ethics, and art kind of covers pretty much all of it. But this idea of trying to come at how does it shape us to use a smartphone every day? What are the implications for how we think about community of doing social media? What are ways that we can use or choose not to use social media
well? And trying to think on those bigger, more structural levels, trying to rigorously engage
those questions. And I think that's pretty much it. Well, that is quite a few things,
but I think that they're all very interesting. So thank you so much, Chris. This has been a lot of
fun. It's been my pleasure. We ran very long,
so hopefully it is not too much for you and your listeners. Thank you so much for your time.
That was the interview. I hope you enjoyed it as much as I did. The last episode with
Cory Doctorow sparked some interesting discussion on our Slack channel and on Twitter. And the Bob
Nystrom interview about interpreters received a lot of attention as well. I'd like to thank everyone who mentioned the podcast on
Twitter or elsewhere. The show went super long, so I don't have time to list everybody's names,
but let's do a couple. Egal Tabachnik, Jeff Martins, Sean O'Shea, Colin Fay, Matthew Staff,
Duncan Adjaye, Tom Mariato, O Fadi, and everyone else.
Thank you.
So let me know what you think of this episode.
I love to hear from people
who have discovered the podcast
and are listening through the back catalog,
especially, you know,
if you'd like to hear your name
or Twitter handle or iTunes name
mispronounced on the podcast.
Until next time, thank you very much