Ben Edelstein: Hello, and welcome to PodRocket. Today I am here with Cory Etzkorn who's a design and frontend at Notion. How you doing, Cory? Cory Etzkorn: I'm doing well. Hi, Ben. Ben Edelstein: Yeah. Well, hello. It's great to be with you. And maybe we could start by just giving like 30 seconds on what Notion is. I feel like Notion's pretty popular nowadays, but maybe case with anyone who doesn't know what Notion is. You just introduce the company you work at, and then we can talk about some of our topics for the episode. Cory Etzkorn: Yeah. Notion is the all in one workspace for notes, tasks, and wikis. So, it's basically combines a lot of tools into one. So, it's like Trello, Google Docs, Project Management, all these things put into one platform. So, basically Notion runs on Notion and you can run a whole company on it and do everything in one place. So, it's pretty cool. I recommend it. Ben Edelstein: Yeah. Well, what we're going to be talking about today is you recently kind of wrote this blog post about how the team at Notion migrated the marketing site over Next.js. And I thought you did a really nice write up. I read it, but it'd be great to kind of talk through that, go a bit deeper, maybe in some parts than the blog post did and really understand the why, some of the technical reasons, some the technical challenges you had during the project, what the learnings were, because I think it's a common situation now with the rise and popularity of Next.js. A lot people are thinking about migrating something that's built in a legacy system over to a modern Next stack. So, yeah, maybe you give us a kind of an introduction to what originally facilitated the thinking. Why did you want to migrate in the first place? And we can go from there. Cory Etzkorn: Yeah. So, I've been at Notion for almost three years now. When I joined, it was, we were still around 15 people, which is pretty small. And the whole marketing site was actually just an extension of the app, because the app is built with React and it's all client side. So, we basically just grabbed components we had in the app and we created a marketing site. We already had a button, we already had a title component and stuff. So, we're like, we should just reuse it. So, that's kind of what the state of things was when on I joined and we worked that way, because it was fast, we had all these tools available, but it wasn't the most scalable for a few reasons. As the marketing site grew, we started to add a lot of new content and started thinking about content management. Cory Etzkorn: We didn't have any CMS originally. We needed to add things like a blog, customer stories, lots of new dynamic content. And we obviously wanted to harness the SEO value of all this content and a client side approach. It could get picked up by Google, but it's still unclear, I think, whether Google will index that type of content, if it's not pre rendered or server rendered. So, yeah, we're looking for a way to generate a site with a lot of content and scale it over time. And we started looking at new solutions like static site generators, hosted solutions, lots of different approaches. Ben Edelstein: Got it. So, basically you had one big static bundle of JavaScript that the client would go to Notion.so, I think is the ... Cory Etzkorn: Yeah. Ben Edelstein: Go to Notion.so and they get a, I'm guessing it was probably a heavy bundle of JavaScript. Cory Etzkorn: Yeah, it was, I have to double check, I believe it was like 9.1 megabytes or something. Yes, 9.1. But that was for the entire app, which like for the entire Notion app is reasonable. But we were loading that for the first visit on the marketing site, just on the homepage and obviously it wasn't necessary. Ben Edelstein: Got it. So, makes sense like early on you kind of, it was easy to start with, you were able to ship it quickly to just be able to use the existing components, have one bundle that's the whole site, but then starting to as a company grew some of the issues with that. And when you thought about what technology to base the new system on, did you look at, in addition to Next, did you look at Gatsby or like a whole variety of tools or how did you kind of do that analysis? Cory Etzkorn: Yeah, we were very close to going with Gatsby actually. It was kind of a last minute decision to go the Next.js route. I think we approach this problem like we approach all engineering problems at Notion with like a RFC and we really try to start from the problem instead of the solution. And I think that's, a lot of times when there's a big migration or like change of technology, it can be driven by you're the developer and you want to use the fancy new thing. And of course I want to use the fancy new thing. I do enjoy that, but we really try to start by documenting what are our actual developer experience problems? What are our actual user experience problems? What are our scaling problems with our current solution? And we kind of got those down first and from that, that's how we started exploring different approaches. Ben Edelstein: And you mentioned choosing Next kind of a last minute thing. What about Next made you choose that at the last minute? Cory Etzkorn: Yeah, I think, I mean, we even considered WordPress, I think I definitely had an inclination to go with a headless CMS and then kind of a generated frontend just because I've built tons of WordPress sites and worked with CMSs where the content is tightly coupled to the presentation. And I've seen that work well initially, and I've seen it scale awfully. So, yeah, we looked at Gatsby and Next.js. And Gatsby, I think Gatsby, we were initially really excited about because it had a lot of plugins and in particular it had a Contentful plugin, which is the content management system we chose. And I can talk more about that choice too later, but Gatsby seemed like kind of a plug-in play thing where it would just sort of work with everything we had and all of our data would get piped into there. Cory Etzkorn: They have like a GraphQL API thing that all the plugins feed in their content to that, and you can query it. So, that seemed nice. But then actually what help make the decision is we just built a very small prototype of the site in both Gatsby and Next.js. And there was slightly more work upfront for Next.js because we had to write our own client for our CMS and write some of our own queries and stuff. But I think the experience, we kind of liked having to do that, because we felt like we understood everything that was happening. Cory Etzkorn: And we felt like if we needed to extend it or change the way content's queried, we could just edit our own code. And we felt a little bit like just given how big our needs were that Gatsby, we could get to a point where we want to do something custom and we have to override some component or plugin. So, Next.js just, it seemed really flexible and it seemed like it kind of let us do our thing, but it pushed us towards decisions that would lead to high performance. So, it guided us, but it didn't really enforce certain ways of doing things. Ben Edelstein: Got it. That makes sense. And when you kind of started the migration, once you made that decision, were you like, did you consider just doing a ground up rewrite or were you always planning to bring over a bunch of components and styles and things from the previous site? Or how did you kind of think about that decision? Cory Etzkorn: Yeah, so migrating the whole site in of itself is just a giant project and our site wasn't even that big. It was like, I think it was like 20 pages or something. And we did already have two locals already. So, there was that added to the mix. But give given just the size of that initial work, we wanted to descope it as much as possible. And this could easily turn into a full redesign. It could turn into a, let's add all these things we never had before. Let's try to launch with the blog and all these new features and migrate the help center. Cory Etzkorn: And we are just like, this is already hard, let's just focus on moving to a sustainable framework that's going to enforce good default choices for performance, something that we believe in that'll scale with us over time and we feel like is improving. Like a framework that's improving. And let's focus on the minimum amount of stuff we can do. And we actually did make it a little harder on ourselves than we needed to. Because there are other changes we made, like we moved from React style props to style JSX for handling CSS. Cory Etzkorn: So, in the React world there's so many ways to handle CSS. And that's definitely a point of contention for me, because CSS is probably ... it's the area that I spend a lot of time as someone who cares about design and front end. And writing CSS purely in JavaScript, it has some limitations depending on what you need to do. There's a lot of features like hover states and pseudo selectors and some more modern stuff that can be hard to do if you're just writing inline styles. Cory Etzkorn: So, we all included style JSX and that made the migration a little harder, because now we weren't just migrating components. We were also rethinking the way we handled styles. So, what could have been just a pulling a components folder over ended up being kind of rewriting a lot of components, but we included that work as part of this migration because we felt it was central to improving the speed at which we could build new things. Ben Edelstein: Right. And I guess at this point it sounds like you're not sharing any shared component logic or component styles between the marketing site and the app or is that not true? Cory Etzkorn: Yeah. So, this was another thing we kind of went back and forth on is should we share things and what should we share. And we settled on not sharing any components. We do share some configuration files. It's still a monorepo. So, the marketing site lives alongside the app. It's just in a separate directory. So, we have kind of an app directory, a marketing directory, desktop app directory. And then we have like a shared folder. And the only thing we really share is configuration and some helper scripts and things like that. Maybe font definitions, but components are totally separate. And the reason for them being separate is the button on the marketing site might look the same as the button in the product, but they often just do totally different things. Like the marketing button is a lot simpler. Cory Etzkorn: You click it and it brings you to a page or you click it and it brings you to a signup flow. But the app button, the app button itself in the product, that component had, it was like 50 different props on it, you click this button and then the Android toolbar appears or you click this button and the database duplicates an entry and a lot more stuff than just listening to on click or something like that. So, we were constantly battling all this complexity that made sense in the app, but was kind of a hindrance for the marketing site where we just needed to move faster. So, that's why we decided to keep components separate. Ben Edelstein: Right. I imagine you may have had some kind of issues with testing and stuff where it's like developers who are iterating on the components within the app. They have tests that they're running on the app, but then it could break something on the marketing site. And so you need a test suite that crosses both, right? Cory Etzkorn: Yeah. So, it is still, this is an area we're still kind of figuring out. So, it is still a monorepo and to merge code into the marketing code base, it's still, we still run all the tests. It's one CI workflow for the app and the marketing site. We updated it recently. So, since they are in separate directories, we do run like a diff check. And we only run the marketing site CI checks if changes were made to the marketing directory. So, that speeds up the deployment of the app. It is kind of nice, because there are some areas where we want to make sure there is consistency. The marketing site does rely on a few server side APIs. And since they're in the same monorepo we can actually use TypeScript between these different directories to make sure that the shape of the APIs we're consuming on the marketing site match the shape of the APIs on the server. So, there are some nice areas where having the CI shared makes sense, but it can also be, can be frustrating. Ben Edelstein: One of the things in the blog post that I thought was interesting is you mentioned how on the marketing site you have an embedded demo of the actual product, and that was made easy by the fact that it's a shared component library. You can just use some of the components for the app. Is that still something that you do and did you have to change the implementation? Cory Etzkorn: Yeah. So, there are certainly advantages to sharing things. Actually, before I even joined Notion, I remember going to the marketing site and on the homepage, you could just start using the app in sort of a little sandbox window. And you could start dragging tasks around on a Kanban board. And I remember seeing it and thinking like, how is this, what is even happening here? How is this all ... I feel like I'm in the app, but I'm not. And I remember kind of inspecting element and seeing, oh, this is all like a React app. It's all the same thing. And that kind of blew my mind. But so yeah, when we launched the static marketing site, we did take out the live demo portion, not because we had to, just because it was another thing that would add to the scope. Cory Etzkorn: We are actually going to bring it back a little bit. So, we just launched a new template gallery where you can, on the marketing site and it's static Next.js pages for each template. And you can search for like a meeting at template or whatever, and you can also click on it and you can preview the template. So, we're going to bring it back. But the way we're doing it now is just like an iframe. And we have basically an endpoint we load it into the iframe where we pass the template ID and you can interact with it. So, it's using the same code as before, except for instead of pulling in that 9.1 megabytes of JavaScript to every page, we're just loading it into an iframe when we need it on a specific page after the user has clicked a button that says, hey, I want to load all this. And try the experience how it ... Ben Edelstein: Got it. Yeah. No, makes sense. And seems like probably worth it for the performance gains to use that approach. Maybe on that topic of performance, what does performance look like now that you're launched on Next? Was it immediate, just boom fast, or did you have to do any work to kind of iterate on performance after you kind of built the prototype? Curious to learn a bit more about that. Cory Etzkorn: Yeah. So, our initial scores when we launched last, it was like last February with Next.js, they were all super good. Performance was like 97 out of 100. Every other score on Google Lighthouse is in the 90 kind of range. But I think the thing with performance is even if you have a really great framework, like Next.js or Gatsby, you have to watch out for it and you have to watch over it. So, all it takes is adding a few third party scripts or some sort of ad tracking pixel thing. And before you know it, your JavaScript is gone up again. So, Next.js is really good about on the page by page route basis and forcing, doing code splitting and keeping those JavaScript bundles down. Cory Etzkorn: But you do still have to be diligent about stuff you add that could affect the whole site, especially to the head section. So, it's, if we don't actively work on performance for two months, it tends to go down and we actually just worked on it for a few weeks just going through what's all the stuff we've added in the last since we launched the site that could be slowing it down. And there's also new features too. So, Next.js releases new versions. And now there's AVIF support for images, which is like an even better image format than WebP. Cory Etzkorn: But that's not something we could just turn on by default, we had to go into the config and say, yes, turn on AVIF images. And in our case, we use CloudFlare for images. So, we had to also write a custom CloudFlare loader that converts our images into this new format. So, a lot of performance gains do come for free over time, but we also have to actively kind of keep an eye on it and as new things come out, implement them. Usually it's pretty quick though. Ben Edelstein: Yeah. And I'm curious, where is the site hosted and how did you make that choice? Cory Etzkorn: Yeah, so currently the site is hosted on AWS ECS, and that is because that's where our app is hosted. So, the marketing site actually has its own Docker container right now, which I did not set up. Another one of our engineers helped with that infra aspect, but we put it on ECS because we already have all the security side of things configured for Amazon. And it was just adding another service essentially. So, we could kind of replicate something we had already made and knew was secure. We really wanted to host on Vercel though. Cory Etzkorn: And the main blocker there was that we have this monorepo and we weren't comfortable sharing our entire app code with the third party. So, we've actually, we've talked to Vercel and it sounds like they're thinking about that problem too. And how you could deploy just a portion of a monorepo to ... You'd build just the marketing site and you'd push up that code to something like Vercel. And they would host it without getting access to everything. So, if that ever is a option, I think we'd definitely consider it, because there's things that would be nice on the marketing side, because marketing just to move really fast and product can too, but marketing there's always going to be a last minute PR news release thing that has to go out or something that just needs to be changed quickly and having things like PR preview links for just for our branch would be super nice. Ben Edelstein: And was there any, when you ... I've never hosted a Next project on Amazon ECS. What was that process like? I guess it sounds like you worked with someone from the infra team to Dockerize and spin up that infrastructure, but were there any additional hoops you had to jump through without using something like a Netlify or Vercel? Cory Etzkorn: Yeah, for sure. I think that we went through a progression of, we really want to use Vercel. The marketing to team wants to use Vercel. And then we kind of talked to the infra and security folks and we're like, how feasible is this without you having to put a ton of work into it? And they're like, kind of a lot of work right now. And so then we started thinking about other options, which were the first thought was this is a static site. Let's build the site and we'll output it into, we don't even need a new service or a new Docker container. We build the static site, we upload it to the existing ECS and the router, our existing router, knows which pages are Next.js. And we just send in there. But then we have a team of content authors now, which is, it's like six people who are writing blog posts, writing help center articles, guides, adding templates to the template gallery. Cory Etzkorn: And the static site has a lot of limitations for on the content authoring side because if they were to publish a new blog post, not only would they have to wait for our marketing site to rebuild, but they'd have to wait for the entire CI to pass for our app. So, we're talking like after things are deployed and caches are cleared and stuff, we're talking like 30 to 45 minutes of time between you writing a blog post and it either being out or you seeing it. But Next.js has a lot of really great solutions for this. They have preview mode where you can basically, it switches from a static site to pulling in client side just for previewing purposes. So, you can click a link in your CMS and you can go to your blog for example, and see the thing you just wrote immediately without and bypass the, you can bypass the static build step. And that's something we really wanted. Cory Etzkorn: That was one thing pushing us towards, okay, maybe we can't just statically generate this. Maybe we actually need some sort of server aspect to this, because we don't have to write any server code for that. But Next.js needs server code to handle the API that triggers this preview mode. But I would say the thing that really pushed us towards having the separate ECS container was localization. And at the time we had English and Korean locals, but we also had plans to add several more and that we wanted to use ... Next.js 10 released localized routing. So, you could do sub path routing, so you can have Notion.so/KOKR/pricing or something, which is really, really good for SEO. Cory Etzkorn: And we also didn't really have a solution for it before. We just had one URL for every local and we just client side swapped out the languages. So, this way we could actually generate static pages for each locale individually. And that feature in particular required having a server. I think I'm not totally sure how it works, but there's an aspect of it that it needs some Next.js API to do the switching. And that was something we couldn't really live without. So, that pushed us towards having a separate service in ECS for the marketing site. And that's something Vercel would include. Ben Edelstein: I'm curious kind of on this subject maybe to circle back, you mentioned earlier, one of the original goals was to be able to have a CMS as part of the new site. You chose Contentful, I think you said. How did you make that choice? What tools did you look at? What are some of the learnings there? Cory Etzkorn: Yeah. I have a long personal history with content management systems, because I've, before Notion and other jobs, I did a lot of freelance web development and I've built probably hundreds of WordPress sites. I've built sites on Craft CMS. I've used a few other hosted solutions. So, I think the thing that I knew for sure that we wanted was we wanted a hosted solution where we weren't going to have to be dealing PHP security holes or patching WordPress upgrades and no shade on WordPress. It's a really great platform. But at the time I was one marketing engineer/designer and we had a lot of content needs and publishing needs and did not want to spend my days doing those kind of upgrades and server side maintenance. So, that left us with a few different options. There was Sanity, which is a really cool ... it's sort of hosted CMS, but you can also do a lot of CLI stuff and really customizable. Cory Etzkorn: And there's a few others that are ... Basically we wanted a content API. So, we build the presentation, we build React components and then we just query for a blog post or something and it returns some JSON with the title and body and stuff like that. And actually my manager, Camille is the one who suggested Contentful. She had worked with it at a previous startup or at first round when she worked there and had a good experience. And then I started, I hadn't used it before, but I started looking around, poking around at other big SaaS websites. And I looked at the Intercom website, I believe, Figma. A few other sites. And I just noticed that a lot of big SaaS companies under the hood, you can see that they're using Contentful. Cory Etzkorn: And it's kind of like, I was probably convinced by just the social proof aspect that a lot of big companies had scaled with this thing. And content is a hard thing to migrate. And it was really important that whatever we chose was going to scale with us as we added internationalization and tons of new different types of content. And I've actually talked to the folks at Contentful too. And it's a lot of people have kind of a hard time. Cory Etzkorn: Yeah. A lot of people have a hard time wrapping their head around Contentful initially, because it's one, there's the headless CMS aspect, which is kind of a departure from I have this theme or something. And then the other aspect is they think of themselves more as like a content API than a content management system. So, a traditional content management system, the content kind of mirrors the structure of the page. So, you have like a title and then, oh, there's two images up here. So, there's going to be two fields to upload images. Cory Etzkorn: And Contentful is structured more like a Postgres database or a relational database where you have a content type called post and you also have a content type called page and a page can pull in posts. And everything is kind of networked in a way that is very engineering-e. And I personally really love that because it plays really well with the component architecture of React where you can compose things. It's very powerful because you can compose content any way you want. You can pull some blog posts from there. You can pull some templates from here and you can pull them all together on a new page. And it's just super flexible. Ben Edelstein: So, earlier you alluded to the fact that you're using TypeScript as part of the new site. Was that something you were previously using and you're just continuing to use TypeScript or was that a new edition with the new code base? Cory Etzkorn: Yeah, so TypeScript, I actually didn't write very much TypeScript before I joined Notion. I mean, it's still fairly new and it just with a code base as large as Notion, TypeScript is pretty critical. And I quickly saw the power of just how fast you can move when you have type safety in your whole code base. And we definitely wanted to continue using TypeScript on the marketing side. And so that was also something that played into our decision for which static site generator to use. And I believe at this point, both Gatsby and Next.js have TypeScript support, but at the time I think I was reading through the Next.js docs and it seemed pretty clear that TypeScript was kind of foundational to what they were building and really important to them. Cory Etzkorn: They had like a TypeScript starter example. And the other area where TypeScript is super critical is since we are using a headless CMS, Contentful, we have this really cool script that it's a Contentful API and this Contentful API returns the schema for our entire content management system. So, it says we have a content type called post. We have a content type called template. Template has a title and it says whether it can be defined or undefined, it says if it's a string or if it's a number or if it's a relation. So, we have a script that we run, it's a CLI command. It's like Notion build Contentful types or something. Cory Etzkorn: And it generates, it hits the Contentful API, and it generates this type definitions file in our code base. So, whenever we make a change to our Contentful schema, we run this script and we instantly know what the shape of our data is on our marketing site, which with Contentful in particular is really important because things can get nested really deep where you have a blog post, but then the blog post pulls in a content type called person for the author and person has a profile photo and they have a name and they have a job title. Cory Etzkorn: So, you can end up with a JSON structure where it's four or five levels deep. And TypeScript, when you're especially in VS Code, you could type post.fields.person.fields.name and that even there is like a lot to kind of remember and with TypeScript you get the auto completion. And also it's, you always forget which CMS fields are optional and which ones aren't. So, it prevents a lot of build errors too, because that's something we've struggled with is since our marketing site is in the same repo as the app, issues with content, like if we accidentally publish a blog post and there's a missing field or something, it can really screw up deployment, because the whole marketing build breaks and then that CI step kind of falls apart. So, TypeScript definitely eliminates a lot of those issues, because we know what the shape of our content is and can anticipate it. Ben Edelstein: Yeah. I mean, that makes a lot of sense. And we've spoken on a couple of different episodes about using TypeScript in a similar capacity. Like for example, with GraphQL, if you have GraphQL API, you can get that type safety across the API layer into your code base because of the fact that GraphQL has types. And sounds like a pretty similar approach here with getting the types from Contentful. Cory Etzkorn: Yeah. We actually, we've wanted to test out GraphQL queries for a long time, especially now that you can have type safety with them. I think, I think I just sort of had like an aversion to GraphQL just because it is like an, can be a lot of a abstractions and stuff. I kind of like just the simple JSON response, but what we've found is that now that we have all this content in our content management system, we're trying to use it in more interesting ways where we have pages that pull in a lot of different content and try to relate it all. Cory Etzkorn: Like a page that's all the content related to Notion databases. And we want to show all the blog posts about Notion databases and all the help center articles. And in those cases, GraphQL could be super cool, because that's a case where we could kind of define our own schema for what we want to query and show on the page. And yeah, if you're querying all those things separately, without GraphQL, you end up with just these giant JSON responses, with you probably don't need much of the stuff in there if you're just trying to show links to a few pages. Ben Edelstein: Yeah. Makes sense. I'm curious to hear a bit about once you built the new site, tested it, ready to launch, how did you do the migration and what kind of changes to your configuration or your kind of service side configuration within AWS did you have to make to launch successfully? Cory Etzkorn: Yeah, so launching, I mean the easiest way to do this would've just been to launch the new site and you could do that, but we get millions of visitors to our marketing site and we have to ... It's pretty risky just to launch a new site, even if we did a lot of QA and testing. I think we would've been more comfortable with just turning on the new site if it was fully static, but since there was also this server side component and it was going to be running in ECS, we really wanted to make sure we're ready for the load of all those visitors. So, the way we set it up is we have like an experiments framework that we use to launch new features in the app. And that is one of the things that's shared between the marketing site and the app. Cory Etzkorn: And it's basically just an API endpoint that you can run experiments and you can put 50% of the traffic here and 50% of it there. And the way we set it up in this case is we created a reverse proxy. So, the request comes into Notion's website. It's like Notion.so/pricing and that hits our API server, which is the same API server that powers the app. And the API server sees a request. And we have a list of, it's a manual list we just keep of all the routes that are marketing routes, Next.js routes. And the API server parses that request, checks that list. If it is a marketing route, it'll just forward the request through this reverse proxy to the Next.js container. And it's pretty seamless for the user. Cory Etzkorn: There's a lot going under and under the hood. And if it's not a marketing route, it'll just the API server will handle it and it'll return a Notion page, for example. But since we did use the same routing and API server as the app, at least for that initial check, we could use our existing experiments framework. So, we just, for 50% of the incoming traffic, we're like, if it matches a marketing route, send it to the Next.js server, if it doesn't just send it to the existing client rendered page. So, that way we could have two versions of our pricing page, for example, running at the same time. And we could slowly scale up traffic to it. And we did it over like two weeks. And yeah, there was no issues with server load and we scaled it up. Cory Etzkorn: And then after we got to a hundred percent, we just sent all the traffic to Next.js and we had the fun task of deleting a bunch of components from our component folder. And that's always a little tedious, but it's super rewarding, I think, to see ... It was really, I think everyone in the company was really excited to see the separation of concerns finally, where it was really clear. Like, hey, if I change something here, it's going to affect the app. Hey, if I change something here, it's going to affect marketing. If I change the color of a button in this marketing folder, I don't have to worry about millions of people having a different color button in their database, in their Notion database. So, I think that was really nice. Ben Edelstein: So, Cory, I guess overall, you successfully launched this project. I mean sounds like you achieved most or all of your goals. It was a big win for the team. For folks out there who are thinking about doing a similar migration, whether it's to Next or a different tool, what are some of your learnings overall tips and tricks or just things people should be aware of if they're embarking on a similar journey? Cory Etzkorn: Yeah, for sure. I mean, I guess we did our best to scope this project down as much as possible. And I still feel like there was a lot. So, just to give you some context, it was about 200,000 lines of code that we didn't necessarily rewrite, but we had to migrate hand by hand. And in the case of React components, we had to switch each component from React style props to style JSX and that's kind of manual. And that was our own choice, but there's 109 React components we migrated. There's 23 static pages, 129 dynamically generated pages and then two locals at the time, which since then the site has just exploded in size. So, I'm glad we did it when we did. And I think that's probably my first piece of advice is I think it's really easy to over-engineer things and plan for future growth that may never happen. Cory Etzkorn: So, if you have a client rendered site and it's serving you well, and SEO isn't really that important, which it isn't always that important. You don't have to go out tomorrow and rewrite your whole site if it's serving you. It served us really well for a long time. But if search engine visibility is critical to your business and you think it could fuel future growth. If your developer productivity isn't as strong as it could be. If you're tired of wrestling web pack configs to get code splitting and things like that. If you're someone who's coming from rails and where you really like the sort of convention over configuration approach where a framework like Next.js steps in and guides you down a really great path, but doesn't totally enforce it. I think all those are really strong reasons for considering something like Next.js or Gatsby and going static route. Cory Etzkorn: And I know the first time I built a static site, it was with Jekyll, like a personal blog and that was probably six or seven years ago. And I remember feeling like it was sort of like a static site was a step back where you can't do server side stuff. I'm like, well, that's kind of silly, but I think just the current landscape of static tools, especially Next.js is so far from that world. And it's come so far where Next.js has API routes where you can run API endpoints in a serverless way. And there's all this stuff you can do that's incredibly powerful. Cory Etzkorn: And it's fast, it's performant, it's user friendly. It's a joy to work with and work in. And it's, I've never really encountered anything where there was going the static site route was a blocker or a limitation to what we could do. It's only really pushed us forward and empowered us to build the best marketing experience we can. And I'm also really excited to try using it for a full app, because you can actually build full apps on top of these frameworks too. I can keep going about that. Ben Edelstein: No, I mean all super valuable insights I'm sure. And I mean congrats on the success of a big, important project. So, I think we're just about kind of coming up on top time on the episode. So, Cory, it's been great to have you on the show, really appreciate your time and we will post a link to your blog post in the episode description for anyone who's interested in reading and getting a bit of different detail than what we covered today. But yeah, it was great to speak with you and really appreciate it. Cory Etzkorn: Cool. Thank you so much, Ben. Thank you Kate, for having me. This is really fun to talk about code and Next.js and content management. Whoo. Brian Neville-O'Neill: Thanks for listening to PodRocket. Find us at PodRocket pod on Twitter, or you could always email me, even though that's not a popular option. It's Brian@logrocket.