Automatically maintaining thousands of code demos across multiple framework variations


At AG Grid, we maintain thousands of code demos, with the same demo in Angular, React, Vue, and Vanilla JS. Additionally, each framework has different variations covering JavaScript, TypeScript, Modules, Packages, Hooks, Classes, Vue 2, and Vue 3. How do we do it? We automate. This session will explain how we start with a single TypeScript version and automatically convert it to every combination we need and finally use Cypress to test the converted example.



Can I ask you a question before I start? How many of you would be happy at your job if they asked you to maintain thousands of code demos? For me, you are happy. That's probably a good job for you in writing docs. But for me, that's not what I want to do. And that's a situation I found ourselves in at AG Grid. Now our aim is to make the best JavaScript data grid in the world. But we understand, as Rich was talking about earlier, that our grid is only going to be as good as what our users can do with it. And the way we can show them what they can do is via our docs. So we are determined for every feature that we have to have a live code demo. So you'll see what the grid does, and you'll also see the code that is powering that demo. So if we just start adding in all these examples for our features, we've actually got up to about 500 demos now. And you think that's bad enough. Wait till you realize that AG Grid is agnostic. So we support JavaScript, TypeScript, Angular, React. You can use hooks and classes. You can write it in view two, view three. And we want you, when you're using our docs, to be able to see the code in the framework that you're using. So you times all these numbers together, and we end up with about 8,000 demos. So there's no way, even if you do want to do it, that you're going to be able to keep up and not make mistakes with copy and paste. And it's just going to be quite boring, to be honest. So in an ideal world, what we want to do is write a single example. And we're going to do this in TypeScript, because for Angular, we need types. If you want React with TSX, you need types. For our JavaScript example, we just strip the types out, and we're done. And then we can use that as a starting point for our view and React with just plain JavaScript. Now the first step to be able to achieve this is to categorize your code. So here we've got some example ag-grid code. We've got some static config. We've got a callback and also an external event. And the reason we need to categorize this is so that we can apply framework-specific converters to each of these specific areas. Because we know that Angular uses different syntax to React, and it's different depending on what you're trying to achieve. But then the challenge and the question is, how do we programmatically categorize our code example? And that's where abstract syntax trees come in. Because we can represent any piece of code as a tree. So for example, the highlighted function here is size to fit. And that's represented as a function definition with a property of name, parameters, and the body. And any code file that you've got, you can break it down and represent it in this tree structure. So for example, if you're using TypeScript to parse it, you give it the source text of the example, and it will give you this AST. And then we're going to traverse this tree and use pattern matches to pull out all of these different sections. So we'll find what's a property, what's an instant method, and what's an event handler. So this is an example of what one of these pattern matches might look like. So we've got, is this a function crawl? So we run through all the nodes in the tree. And if it's a function call, then we pull it out into this external event handlers, where we've got the name, the parameters, and the body. So we build up this memory model of what the code example actually looks like. And then we use our framework-specific converters. So here we can see Angular and Vue. You've got the different syntax for trying to achieve a similar thing. And so we use these framework-specific converters against each category. And once you've done that, you can spit that code out into a base template, and you find you've got your Angular, React, and Vue examples. And we have only written the TypeScript one along with these converters. Just to make sure we haven't made a mistake, we validate it with TypeScript. We hit every single one of these examples with Cypress to make sure there's no errors. A bit of snapshotting for visual regressions. And if we're feeling really keen and careful, we can Git diff the entire repo to make sure we haven't done anything silly. And the result for us is that we're able to update all our framework examples in one place. For example, we added support for React and TypeScript, and it only took a day, but there's 500 examples there. If we want to add a new framework support, we'll just add one converter. And we're happy developers, because we're not maintaining all of these examples, and there's more time for us to work on features. And I believe our users are happier, because they can go to our docs, say what framework they're using, what variation they're using, and they get to see the code that matches that to copy and paste into their application. So for you as takeaways, I want you to realize this point that we can use TypeScript abstract syntax trees to generate and reason about our code. And a great way to get started with this is Just drop your code into that, and it will show you what the abstract syntax tree looks like. And finally, don't manually maintain something that you can generate. If you do take inspiration from this work, I'd love to hear what you end up building with it. Thank you. And I'll see you next time.
7 min
16 Jun, 2022

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

Workshops on related topic