TypeScript Expert Interviews Bonus 11 exercises
interview

The TypeScript Culture at Formidable with Kadi Kraman

Kadi Kraman, Director of Engineering for Mobile Services at Formidable, discusses TypeScript and its use at Formidable for building modern web and mobile apps.

At Formidable, the engineers are not mandated to use TypeScript, but they gravitate towards it because it is the best tool for the job. The

Loading interview

Transcript

Matt Pocock: 0:00 What's up, Wizards. I am here with Kadi Kraman, Director of Engineering for mobile services at Formidable, extremely experienced React developer, my friend, and someone who is, I think, about to get a dog.

0:14 We're here to talk about TypeScript at Formidable, talk about Flow with TypeScript or what happened to Flow, really, because you're an extremely experienced Flow person. Also, how a big agency like Formidable runs TypeScript and uses TypeScript and its history with TypeScript.

0:30 Kadi, hello. Please introduce yourself to the kids at home.

Kadi Kraman: 0:35 [laughs] Hi, Matt. Thank you so much for having me. As you said, I'm Director of Engineering at Formidable. I've been working with JavaScript for, oh dear, it must be coming up to seven, eight years.

Matt: 0:51 Wow.

Kadi: 0:53 The past five of those have been in typed format, one way or another, so lots of fun in JavaScript world.

Matt: 1:03 For sure. What is Formidable? Let's start there. Formidable, I know it by reputation as an amazing agency to work for and a really cool place to...You guys are doing a lot of cool work, especially in the React Native space, right?

Kadi: 1:21 For sure. Formidable is a JavaScript consultancy. We're a digital agency. We do websites, mobile apps, APIs, design, but we specialize in JavaScript, or, I guess, TypeScript these days.

1:40 Our projects tend to be in JavaScript stacks. We'll use, at the moment, for example, Next.js and React on the frontend. We'd usually use some kind of Node.js API. We would use a React Native. We build all kinds of things for the modern Web and mobile, but they tend to be in JavaScript.

Matt: 1:58 What's your personal history with TypeScript, let's say. Before we start talking about Formidable's history with it, how did you get into TypeScript? How did you first hear about it? What was your feeling when you first used it?

Kadi: 2:10 My first intro to types was actually from Flow. A lot of React, or React Native engineers would have tried Flow before TypeScript because Flow comes from Facebook, as a lot of other tools that we use when we're in a React ecosystem.

2:30 Facebook comes up with...We had React. We had GraphQL. Then Flow comes out, and of course we're going to try it as well. I'm pretty sure the React Native template project still comes with Flow types, so that was an easy intro for a lot of us. My first experience with types was with Flow.

2:50 Then the community sentiment started shifting towards TypeScript. That was maybe five or six years ago, and the TypeScript community continued to grow.

3:05 My personal experiences with Flow were sometimes frustrating, so in the subsequent projects, I tried TypeScript and was hooked from then on.

Matt: 3:16 What about it hooked you? I guess you'd had that experience of types in JavaScript from Flow, so why don't we start there? What was different about the experience of introducing types into JavaScript that hooked you?

Kadi: 3:30 The difference between Flow and TypeScript is that Flow is like ESLint for types. To opt in to Flow types, you add a comment at the top of your file. I think it was @flow. Then you can add Flow types and you'll have some static type checking as well as explicit types that you can add.

3:54 At the compile step or at the build step, at the transfile step, you'll just strip out those Flow comments, whereas TypeScript is a superset of JavaScript.

4:07 To convert your code base from JavaScript to TypeScript, you just change JS to TS. That's already valid because all valid JavaScript code is valid TypeScript code, so it's much more integrated and built in. It's easier to configure.

4:26 I don't know if you recall seeing Flow config files, but they were massive and [laughs] quite confusing. I did a talk about Flow versus TypeScript years ago. In one of the slides was a comparison of a Flow config file and then a TypeScript config file.

4:45 TypeScript config obviously is just some adjacent object. You're like, "Oh yeah. It's so readable." [laughs]

Matt: 4:51 That's funny because I think a lot of people these days would say TypeScript config files are getting too big. That's funny that Flow was a less mature project than TypeScript at the time. It came out two years after TypeScript. Am I right in thinking that?

Kadi: 5:05 I don't think I know the dates, to be honest. I think that the difference, the thing that convinced me was more the community support. Again, actually, I looked up this slide because I was anticipating this question.

5:21 This would have been in 2019, in April. For both Flow and TypeScript, there are libraries that you would use in open source that don't have types. Then community would create those types and then share them.

5:40 For Flow, this was called Flow-typed. It's a big monorepo of types. Then for TypeScript, it was DefinitelyTyped. At the time of these slides, the Flow-typed repo had 3,000 stars, and the TypeScript repo had 21,000 stars. Obviously, GitHub stars don't mean everything, but they mean something.

6:04 That order-of-magnitude difference indicates how the community is more there for TypeScript, which makes it easier to adapt. The likelihood that you are going to install a public JavaScript library and there will be types for it is bigger. It's just easier to work with.

Matt: 6:26 Let's start talking about Formidable's approach to TypeScript and to Flow. I think Formidable is interesting as a company because you guys are very opinionated about the stacks you use.

6:39 There's a lot of open-source work done at Formidable too, which is different to some of the agencies that I've worked at, where it was mostly just about getting the work done as fast as possible and not caring too much about code quality. You cared about code quality so that you could sleep at night, as opposed to it being mandated from top down.

7:01 What's the current attitude towards TypeScript now at Formidable?

Kadi: 7:07 TypeScript is absolutely the default for all new projects. I would be surprised if anyone was against it. We've discussed it. I think a lot of us have had the same experience with TypeScript, especially people who were experienced with plain JavaScript beforehand.

7:26 You're really good at plain JavaScript, and then this TypeScript thing comes along, and you're like, "Oh, bloody hell. I had all these types, and I have to learn them. Why do we need another thing, another build tool? We get build setup fatigue." All that kind of stuff.

7:41 Then you get through the hump, the learning curve of TypeScript, and you become comfortable with the syntax. It maybe takes a couple of months and one big refactor that you had to do in your code, in which the types told you exactly what you need to change.

8:00 After that, no one wants to go back to plain JavaScript. I think that's the sentiment we pretty much all share.

Matt: 8:08 How long has that been a case at Formidable? When did that decision get taken? How is that mandated? Are you telling people, "OK, you need to use TypeScript," or are the engineers saying, "We want to use TypeScript"?

Kadi: 8:23 We don't tell our engineers to use one thing or another. I feel like it's almost a community sentiment. Maybe it's like a self-fulfilling prophecy, but I know that you mentioned the open-source work that we've done. I do agree that a lot of companies...because it's quite rare to actually see so much open source.

8:48 I think it's self-fulfilling because we have always done it, and therefore we will attract engineers who are interested in doing it, who will then create more of it. It feeds into itself. In general, what unifies engineers at Formidable is we want to do the best thing the best way that we can.

9:12 TypeScript just, at the moment in the JavaScript community, is the best tool for the job. That's why we all gravitate towards it.

Matt: 9:22 It's a TypeScript culture, basically. You attract engineers who buy into the idea of building something robust, and then it fulfills itself. I imagine Flow at Formidable was something that was interesting and something that people were using. When did Flow start to go out and TypeScript go in?

Kadi: 9:45 I think it was around 2017 that the first TypeScript projects cropped up. Bear in mind, we are a consultancy.

9:57 The toolings that we use, the specifics of toolings, we would always use React and Node.js, but the specifics of whether it's JavaScript or TypeScript or Flow, and whether we use GitHub or Bitbucket, that kind of stuff does come from the client, so we need the client to align as well.

10:17 The first purely TypeScript project started cropping up around 2017, although I personally was on a Flow project at 2017. After that, I was in TypeScript.

Matt: 10:29 Interesting. That's a long time ago. Wow. You guys have had it for a long time. Considering TypeScript has been around for about 10 years, you've been 6 out of the 10 years, you've had it at Formidable. It's crazy.

10:40 How is TypeScript integrated at Formidable? What do you do in terms of CI/CD processes? Do you use TypeScript as a linter with noEmit, or do you use it to build your projects? How does it work?

Kadi: 10:55 It depends on what you're building. We don't have a company-wide recommendation of this is how you do TypeScript projects, because it depends on whether you're doing a front-end, or a backend, or a mobile app.

11:11 In general, our approach is to use the platform defaults for whatever you're using. If you start a new React Native project, for example, with the TypeScript template, you will get a tsconfig and then we stick with that tsconfig and maybe expand it based on the project's needs. I would always set up modulators, for example.

11:36 It's the same for Web and same for API projects, and you do use the platform-specific recommended defaults.

11:45 The only thing we would recommend is to always use strict mode. If you start a new project, there is no excuse not to use strict mode. The second thing is to always integrate it with CI. If you're on the Web in API, if you use TypeScript compiler to build a project, then that will do the type checking as part of your CI/CD process.

12:12 In React Native, it can be a little bit different. In React Native, we used the Metro bundler to bundle the JavaScript bundle, and then that just strips the types away. We would add an explicit tsc, the compile step on CI with noEmit, and then that will check it.

12:32 It would be if I set up a new CI/CD process for React Native, I would do a yarn install and then I would do ESLint, which would do linting in Prettier, and then I would do tsc for the TypeScript check, and then I'd run the tests.

Matt: 12:51 That's the process then. You essentially treat TypeScript like a linter, not building too much with it on React Native, right?

Kadi: 13:00 Yes. Yeah, but that's very React Native-specific. On the Web and API, when you'd use the TypeScript compiler to actually compile your bundle, then your type checking is part of your CI first as you can't deploy your app if your types aren't right. It's nice that it's just part of it. You can't really opt out.

Matt: 13:24 It's so nice. With PRs as well, you can't mode your PRs unless all the linting checks work and that kind of thing. Yeah, that's lovely.

13:33 That's sort of becoming the standard way. I've asked this question a couple of times, different people. That's becoming, I think, the standard community way of doing it. There's a couple of different approaches you could take.

13:43 You could do it on a pre-commit hook, let's say, and run linting there or run testing there, or you could do where you block your dev server, so you can't even see your dev server, especially on a Next.js project.

Kadi: 13:55 [laughs]

Matt: 13:56 Yeah, right. The testing part, what's your attitude to that?

Kadi: 14:00 You can see from my reaction that I [indecipherable] that. There's nothing more frustrating than if you've got TypeScript enabled in your test file. You can't run your tests, because you have an unused variable or something.

14:18 You just can't, because as you're building something, you're going to be commenting things out, console logging stuff. You're just going to have things you're never going to commit but you need that interstitial step for debugging, cost, and that.

14:32 As for pre-commit hooks, I'm actually pro pre-commit hooks. At the moment in my project, we've set up a pre-commit to do linting, and then pre-post to run tests. Obviously, if at any point, you're in a rush and for some reason don't have the time to wait for the hooks to run, you can always do --no verify, and that skips that check.

15:02 It's not like you have to wait this extra, if you have a little test, like two minutes, in order to push. You could still omit it...

15:10 [crosstalk]

Matt: 15:10 Yeah, for sure. I guess that's running on CI as well, so you're double checking when it gets to the CI point too.

Kadi: 15:15 Exactly. It tends to be more handy if your CI process is long, or cumbersome, or maybe it takes a long time to build your bundle or whatever.

15:29 You'll do a local check first so you wouldn't have to wait for everything to get spun up on your CI, and you're like, oh dear, it fails. Now I have to pull it back down again and fix it, and then check it, and then push it. You're just saving yourself time.

Matt: 15:43 It's a feedback loop thing, basically. It's like you're shortening the feedback loop from when they understand that the PR could be merged, let's say.

Kadi: 15:51 Yeah, exactly.

Matt: 15:52 Nice, but situational, I guess. If your CI is running pretty quickly, then you probably just use the CI maybe, or you like a pre-commit hook?

Kadi: 16:03 I don't know. It depends. I actually use both. I'm just trying to think. I would say that about 70 percent of the time, I would let the hooks run.

Matt: 16:12 Nice.

Kadi: 16:12 Not for everyone.

Matt: 16:15 Thinking about your attitude to the dev server thing, which I totally agree with as well, you do not want TypeScript interrupting your ability to run your tests or messing things up for you locally. That's an interesting advantage of TypeScript, which is that it can compile while still having errors, right? It can still run even though the types aren't perfect.

16:41 That's great when you're using a bundler like Metro, or esbuild, or SWC, or something that just doesn't do type checking.

16:47 I'm really interested in comparing it to other languages which are more strict like Elm, or ReasonML, or things. I went and watched your talk from 2019 in React Finland, the one you were talking about with the config files and stuff. Those were proposed back then as an interesting alternative option.

17:08 I wonder how those languages...Could you give a little intro maybe to either one of them and talk about what they do in comparison to TypeScript? Let's start there.

Kadi: 17:18 Yeah. Similarities are that they are a way to build front-end applications, stuff that runs on the Web, websites in particular, in an alternate way, alternate to plain JavaScript.

17:41 In order to run on the web, you need to have an output that's in JavaScript. Both of those languages will let you write your code in something else, but then you can transpile that into JavaScript, stick it on a browser, and then be able to build your website. That's the similarities.

17:57 Differences are that the type checking is done at the compile time. There's two types of languages. You will have type language...Sorry.

18:09 There's two types of programming languages. All languages will have types, but the difference is when the type checking is done. Type checking is either done before the code is actually run or after the code is run.

18:24 For JavaScript, the type checking is done after. You will only know if your types work when you run it in a browser and you say that undefined is not a function.

18:37 Adding posts to JavaScript means doing the type checking before. For TypeScript, we're just checking before the code is run, where types are going to be right. For Reason and Elm, for both of them, the type checking is done before you run the code.

18:53 For Elm, in particular, it's a different way of thinking about how you write Web applications. It's very opinionated. There is one correct way to do things. For example, the Redux system comes from the Elms internal. It's inspired by the Elms internal state management.

19:26 State management in Elm is identical, pretty much, to Redux because it inspired it, but it is quite difficult for JavaScript engineers to wrap their head around because the syntax is completely different.

19:44 The similarities are that you can output things that run on the Web, and everything else pretty much is different.

Matt: 19:51 Interesting. I guess the situation has changed now. In 2019, I remember when they were getting popular, ReScript, ReasonML, and Elm. They were seen as, "Ooh, that's an interesting alternative, maybe something to mess about with.

20:08 How do you think has the landscape changed now? Has TypeScript eaten some of their lunch? Does it make sense now to have Elm and ReScript when you have TypeScript now?

Kadi: 20:23 It always makes sense to have more options. I think the reason that TypeScript has taken off more so than Elm, for example, is because the barrier to entry is much, much lower.

20:39 The experience of working on an Elm project, it will be about two months of frustration while you get your bearings, you figure out the one true way, within the context of this project, to do things.

20:52 Then you can work on that project, whereas for TypeScript, pretty much anyone who has experience with JavaScript can come on a TypeScript project. Then, after a bit of googling with the type syntax, within a week, you'll be super productive.

Matt: 21:10 I guess with Elm as well, you're paying the cost of not only having a typed system, but also a very opinionated system on top of that. The Redux mentality on top of it, where it's like, send an action to a thing. That thing then...You got this funky flux loop.

Kadi: 21:27 Exactly. JavaScript, for better or for worse, we can do anything, which means that it leaves room for lots of schools of thought. You can choose not to agree with one way of doing things, and do things in a different way, whereas in a language like Elm, if you don't agree, you go and write it in TypeScript.

Matt: 21:52 That kind of unopinionatedness or flexibility of JavaScript, let's say. When TypeScript first came around, it had this reputation for being quite object-oriented. Microsoft has made this language, and it's, "Oh, it has interfaces in it, and it has classes as a first-class citizen. Oh, it's just like .NET."

22:17 That was something that I noticed on one of your slides in 2019 as well. TypeScript has a feeling of being object-oriented, which is slightly different from the more React-y way of doing things. Does that still feel like something that you'd agree with now, or has that perception changed for you?

Kadi: 22:37 I think you can choose to write TypeScript in an object-oriented way or not. That's the beauty of JavaScript and, by extension, TypeScript. My very first project with TypeScript, the back end for that project was .NET, so a lot of the team was .NET engineers. .NET itself is very object-oriented, so then that bled more to the front end as well.

23:05 You can write TypeScript in a very functional way, so I don't think it necessarily has to be object-oriented. For example, I personally never use interfaces on my TypeScript projects.

Matt: 23:24 Nice. You're pure types.

Kadi: 23:26 Yes, exactly.

Matt: 23:28 It's a question I get every time I tweet or anything. It's like, "Types or interfaces, types or interfaces?" You're on the types version. You're anti-interfaces.

Kadi: 23:37 Definitely. What I don't like about interfaces is that I don't like that they magically combine. If you have two interfaces, then what you really get is a union of those interfaces with that type, but then I could easily just miss the second declaration of that interface. Then I'm like, "Where did that type come from?"

24:13 It's the same as object-oriented thing. When you have classes with inheritance, and they inherit from each other, and then you are like, "Oh, where does this function come from?"

24:22 It's like a parent of a parent of a parent or a child of a child of a child, so you have to go several levels deep into the base class that this gets inherited from. I don't personally like programming in that style.

Matt: 24:41 I feel like the more senior that you get as a developer, the more scared you get of magical things. The slightly magical, weird properties of interfaces, even though they're not very dangerous -- you do get some warnings, like if you declaration-merge accidentally -- even that is just enough to put you off and just use types instead.

Kadi: 25:05 Yeah, pretty much. I think the other thing that you get with more time writing code is you start off just trying to make it work, and then after some level of experience, you feel like you know what you're doing. Then you get into trying to figure out really clever ways of doing things.

25:26 You get into making everything super dry, don't repeat yourself, making it super generic. Then you get into functional programming, where you are like, "Oh, I can write this thing in 1 line of code instead of like 10."

25:41 Then after some time, when you've had enough experience having to parse this code from yourself in the past, you start to value simplicity. You would rather have five lines of code, which, three years from now, you look at and you know exactly what it does, than a really clever one-liner. Just simplicity and making things easier for other engineers.

26:09 Also for me personally, because I work at a consultancy, I'm always conscious that, eventually, someone else is going to maintain the stuff that I write. I want to make it as easy as possible for them.

26:23 They're not going to be looking at my one-liner and going like, "Oh, this part from [indecipherable] is so clever. Look at this one-liner." They're going to be swearing at this idiot who couldn't write a simple function, so that's what I try to do.

Matt: 26:38 That's a great link, because you gave a talk, I think last year, at React Finland again, your favorite conference.

Kadi: 26:45 It is my favorite conference.

Matt: 26:48 Is it? I haven't been, I need to go. I've got two friends in Helsinki, actually. I really need to go. You spoke about good code, what is good code. Especially in the context of Formidable, good code, it sounds like, is code that can be easily passed on, easily understood.

27:06 I'm really interested in your take about how TypeScript, especially, helps with that.

Kadi: 27:14 TypeScript helps you communicate intent, for one. That's especially handy. The potentially obvious thing is that if you change something, it will expose all the other parts in the code base where that change is needed.

27:37 For example, right now I'm working on a mobile app, which is one mobile app, but we use the same app for lots of different regions. We have six regions at the moment.

27:49 If you add a new region, you will need to make sure that the currency is supported for that region. You'll need to make sure that all the links for -- it's a shopping app -- return policies are supported. You need to make sure that the configuration for payment providers is supported.

28:07 If I was to leave this project, and then someone else was to come in and need to add a new region to this app, because we have typed everything, then all they need to do is, in our types file where we have supported locales, they will add in a new locale.

28:27 Let's say that it's en-US. They add en-US there, and then if they just run a tsc to check the TypeScript types, it will highlight every single place in the code base that they need to change in order to support that new region. Then after they've done that, it will just work. It sounds a bit magical, but I know that it works because we've used it ourselves now.

Matt: 28:54 I'd love to dive deeper into that example because that example sounds amazing. One really amazing use case for TypeScript is configuration files. It's making sure that the config files are correct in your project.

29:10 Other examples of TypeScript working well, like utility functions where you have nice, generic utility functions, but I feel like config files especially are really powerful with TypeScript. It sounds like you're using a union type there, and then that's shuttling down different levels.

29:29 How do you design the structure for that when you're thinking about those configuration files and how they work?

Kadi: 29:36 Mostly, we'll have a union type for a locale. The locale will be en-US, en-GB, ja-JP, and then that's just a union of locales that we support. Then, for example, let's take a currency map. It's an object with key-value pairs, and then we would type the key as, for each key in locale, we'll have a string.

30:06 It will be a locale string map. Because it's typed to be that, if you add a new locale to the locale type but you haven't added a key with that locale in your currency map, then you're going to get a TypeScript error.

Matt: 30:24 You design it where you have the type at the top level, and then all of the config derives its shape from that union. Then that shuttles down into further levels as well, I imagine. That sounds great.

30:39 I talk a lot, in Total TypeScript, about deriving versus declaring, which is, if you have just one spot where your type is declared, then everything else derives from that. That sounds like a really powerful paradigm.

Kadi: 30:56 Definitely. You can do very clever things with TypeScript, as I'm sure you know better than me. A locale is a language and a country. With TypeScript, you can actually strip just a country or just a language from the locale string, so you can have a type that is all the languages.

31:22 Say that we have en-US and ja-JP, so we have English and Japanese. If we need something that supports all the languages, not all the locales, then we can infer a type from this locale with all the languages. Then we can use that type to create another object that's just a language map.

Matt: 31:46 Perfect. I get asked a lot, "How do you apply these more advanced concepts in application TypeScript?" That sounds like a perfect case, where you have just one union that has all of the little bits in it, and then you're using, I imagine, a template literal type to extract out the little prefixes and stuff.

32:07 Other than that, can you think of any other cases where you've used more complicated code or more complicated type code, and what's your attitude, in general, to it?

Kadi: 32:18 Translations is quite complicated. We have translation strings for each region, and they are based on the locale, not the language, because en-GB is not the same as en-US. You will, for example, use template literals to put things inside a language string.

32:47 For example, you have X items in your basket, and then this is in one way in English, but in another language, you would put the X in a different part of the string. You need to be able to input things here. We have a POC for adding types to those template literals as well, which is quite tricky.

33:13 Of course, GraphQL. [indecipherable] GraphQL. The awesome thing about GraphQL is you query the things you need, so you don't necessarily query everything. That also means that if you know that your user type has 200 fields, you can't use the type from your schema as is because you might not be querying all of it.

33:37 You have a step to generate the TypeScript types exactly from our queries, and then we always use the generated types so that we know that they will exist. It's quite handy to use union types for that as well.

33:59 This is a shopping app, so we have an item that could be in a cart, so a cart item, product in your cart, or you can have the same product in a wish list. Then from a GraphQL point of view, the wish list item and the cart item aren't the same type. However, you have an overlap, and the UI looks the same.

34:21 As a developer, what I can do is I can just...You have a product item, and then the type of the item is either a cart item or a wish list item. Then inside the component, as long as I use a key that exists on both, TypeScript is totally happy.

Matt: 34:39 That's so cool. That's really smart.

34:43 That's, again, deriving versus declaring, because you're basically saying, "OK, we have this GraphQL query here. We're generating the types from that." Then that becomes essentially the source of the information, becomes the source of that. Then you pump that directly into your components. That's so clever, using a union type there too.

35:05 I'd love to dive into Codegen just a little bit before we wrap up. What sort of benefits have you seen from using that approach?

35:13 I think there's a lot of people moving away from Codegen steps now and moving into the more tRPC area, where there's no Codegen step and you just get it all through inference.

35:24 I've worked on projects before with GraphQL Codegen, which might be the thing that you guys are using. It's like a CLI that lets you get types from GraphQL files. It's been amazing just to see all the stuff you get for free from it, and you get this type safety on stuff you wouldn't otherwise.

35:43 Also, then, I ended up restarting my TS server quite a lot and doing a lot of...It felt like I was having to work around the Codegen steps quite a lot. What's your attitude to that?

Kadi: 35:58 You do have to rerun things. If I edit my GraphQL query, then I would need to rerun the type generation to update the type. That is a valid trade-off for getting type safety, I think. I'm not sure what the alternative would look like.

Matt: 36:23 I'm not sure there is one, really...

Kadi: 36:24 [laughs]

Matt: 36:25 especially when you're working with GraphQL when you have files that TypeScript can't pass. I've heard of this thing called type providers in a language called F#, where you can basically get automatic inference based on files that aren't written in this language called F#.

36:43 You would actually change your GraphQL file, and then it would automatically infer into the types. We don't have that in TypeScript, so I guess that's an imaginary alternative. GraphQL Codegen sounds like a good alternative if you're doing that stuff.

Kadi: 36:58 It's not like it takes a long time. Basically, I haven't found it frustrating, to be honest. You can do more, potentially, if you have a monorepo so that your API is in the same repo as your frontend code.

37:17 We don't have that, so our step is actually we update our schema so we have a script to sync the schema file based on the latest...based on the introspection query. Then we commit the schema file.

37:35 For the TypeGen, we don't actually commit the types, but it's looking at the GraphQL directory, and it's looking at the schema. We're generating the types based on that, and we don't commit that. That's in gitignore.

37:51 It's been working very nicely. I don't really have any complaints, to be honest.

Matt: 37:55 Sounds great. That's often a problem. When you're in a monorepo, you can do more inference between frontend and backend, right?

Kadi: 38:03 Yeah.

Matt: 38:03 If you're using Next.js, let's say, it's actually in the same package.json directory, so you can infer from one to the other.

38:10 Do you have any tips in general if you're working with a REST API or something that isn't or you're having to work with external APIs that are not in your repo? What's your experience of using that with TypeScript?

Kadi: 38:26 Whenever I work with something that I haven't written, so if it's an external API, it doesn't have any strict types, you can't...I was going to say you can't trust them, but that sounds a bit mean. [laughs] There's no guarantee that the types are what they are meant to be.

38:47 If you work with a GraphQL API, if the types don't match the schema, you'll just get an error. You know that if you're getting something back, they're going to match the types.

38:54 With REST, there's no guarantee on that. You can get something completely different back. Generally, because you have to explicitly type things in REST, I would always type defensively.

39:14 Even if you are 90 percent certain that this field will always exist and it's going to be an array type, I would always make it optional so that when you're requesting something, you're going to call to JSON on it, you get the body.

39:33 Then if you're calling something .map, then TypeScript's going to make you put a question mark there so that if it's not an array, you're not going to get a runtime error, basically.

Matt: 39:46 I guess the alternative is using something like Zod or Yup, which would properly pass that and make sure that everything is as it says it is.

Kadi: 39:54 True.

Matt: 39:56 I guess it's like, "How strict do you want to be?" Obviously, if you're using Zod, then it might slow things down a little bit. Whereas if you're being a bit more defensive on the consumer of the API side, then you get a bit of that too.

Kadi: 40:10 To be honest, I mostly work with GraphQL, so I don't need to think about it a lot. It's only if I'm doing API work and the API's calling other APIs. There's definitely options out there.

Matt: 40:27 For sure. Nice. That's so nice being able to work with GraphQL all the time, actually. I remember working with REST. I primarily used REST when I was at this agency a few years ago, and it was a nightmare not being sure quite what the API was going to return.

40:46 Like field, it was being built as we were building the frontend, and fields were just misspelled or didn't exist or were unexpectedly nullable. We weren't even using TypeScript at the time as well, so that was a nightmare.

40:59 Anyway, sorry, just sharing my pain here.

Kadi: 41:01 [laughs] That is quite all right. To be honest, as with anything, working a lot with GraphQL at scale, you will also see the cracks in the approach. Nothing's perfect, but I think that's a conversation for another time. [laughs]

Matt: 41:20 Nothing's perfect. What a lovely note to end on...

41:22 [laughter]

Matt: 41:22 so optimistic. Thank you so much for this conversation, Kadi. That was so cool to hear about your experiences with Flow and all of the different options that are out there and Formidable's experience with TS, all the CI/CD stuff and all the GraphQL Codegen stuff, and how you guys are using deriving versus declaring. That's so cool to hear about.

41:43 Thank you so much for your time. How can people find you?

Kadi: 41:46 Oh, you can find me on GitHub, on Twitter. My handle tends to be kadikraman.

Matt: 41:53 Nice. We'll have that in the notes below. Thank you so much.

Kadi: 41:56 Thank you for having me, Matt.