Abstract

How much work maintaining the dependencies is too much? How big must be the node modules directory to start bothering you? Do you like spending an hour every now and then to adjust your code to the APIs you depend on, just to have up-to-date libraries? Are those breaking changes enough improvements to warrant you going back to your side project from a year ago just because a new vulnerability surfaced? Many of us don't realize that other people's code in our dependencies is still our liability. I rewrote my blog from JavaScript (Astro) to Go and now I have over 50 times less code I depend on.

An easy way in

When I started developing my blog, I decided to use Astro, as I was somewhat familiar with React. I thought about using other frameworks like Next.js,1 but Astro seemed at the time the best for generating static websites without unnecessary client-side JavaScript. It was actually my second iteration, because the first one was in Python and Django framework.2

Astro3 is an amazing framework for static site generation. It supports multiple UI libraries for defining components, as well as its own component templating language. I obviously didn’t choose React for my side project, that would be too uncool. I chose Solid4. The Developer Experience™ was not bad at all, and I made myself a nice blog renderer, together with a Continuous Delivery™ pipeline, so the only thing left was to write great blog posts in Markdown. Easy enough.

Prepare to move with your framework

Library authors have every right to break all the APIs they wish. Just look at open-source licenses. Every one of them has a lack of warranty disclaimer. Here’s an example from the MIT license used by Astro:

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

I’m very grateful for their work, but that means if I want to be up-to-date with this framework, I have to be prepared to make changes. Changes probably leading into a better DX™, but changes nonetheless. And that means time. Worse, that means switching my context in my free time from reading, writing, or watching ThePrimeagen’s hot takes, to updating my dependencies.

A recent, third major release of Astro brought many amazing things,5 but unfortunately I won’t benefit from any of them in my blog. What I will get instead, is a couple of hours spent reading their guide on deprecations and breaking changes6 to make sure my blog still renders with Astro 4.

I didn’t have to wait that long for the next major release of Astro — version 47 came out on December 5, 2023. Because Astro 3 came out on August 30, 2023, it is only 3 months between major updates. I consider my hypothetical maximum writing capacity to be one article per month. That would make me do a major framework update every third article! I already have enough reasons to procrastinate on writing articles, and another one would probably grind my writing to a halt.

You can soften the blow of upgrading your dependencies with a help of an auto-updater like Dependabot,8 or Renovate Bot,9 but if some library introduces a breaking change, you’re out of luck. The best thing it can do then is to open a Pull Request, and it’s up to you to have a sufficient build pipeline to show you that this change breaks the project. The burden of reading migration guides and manually make changes in your code still lies upon you.

Unplugging from the JavaScript Matrix

Frontend development has become synonymous with JavaScript. In my professional experience, every piece of frontend code I wrote involved JavaScript, so I chose Astro instead of Hugo.10

Only when I learned about HTMX11 I realized that I can generate my static site in whatever language I wanted. Even though I didn’t need to use HTMX for my blog, through their essays and memes12 I convinced myself that the JavaScript ecosystem is not what I want and need. I wanted a break from it all.

I was already familiar with Go and had used it for many side projects and I always loved how stable and extensive the standard library is. It has an HTML templating language built-in, so it seems to be a perfect solution for generating a simple static website.

Minimal dependencies

When implementing the custom blog renderer, I wanted to depend only on the standard library and as little external libraries as possible. Additionally, I wrapped each library into its own module, so that it wouldn’t spread into my application, and it would be less painful to change if I needed to. A thing that was extremely hard to do when using Astro or any JavaScript framework like React or Solid.

I used about 3.5 megabytes of dependencies: 6 direct and 4 indirect, making it 10 in total. In contrast, when using Astro, I used over 200 megabytes of dependencies: 12 direct, and a whopping number of 768 indirect, making it 780 in total. That is 58 times less code and 78 times fewer dependencies.

Let me say that again: from over 200 MB to about 3.5! If you wanted proof that the JavaScript ecosystem makes it extremely easy to just add new layers of abstraction ad infinitum, here you go. There’s and added benefit that having less code executing makes for less resource consumption, and in my case, less Virtual Machine minutes spent in GitHub Actions.13 I’ve never actually exceeded my GitHub Actions quota, but that’s a nice thing to keep in mind. The difference is colossal, the graphs show it better than words.

Comparison of the size of dependencies in Astro and Go implementations in megabytes
Comparison of the number of dependencies in Astro and Go implementations

But there is a catch

Giving up those many layers of abstraction lead to more application code, though. With Astro, I needed 2209 lines of code to implement my blog, including all the JSON configuration files. With Go, I needed 3309 lines of code, so quite a lot more!

The bigger number isn’t at all strange given that I needed to implement a myriad of features that were already ready to use in Astro, like:

  • Parsing Frontmatter for all articles,
  • Sorting the articles based on the date in the Frontmatter,
  • Copying static assets to the dist directory,
  • Formatting the generated HTML,
  • Re-rendering the changed pages.

I also didn’t include some of the Astro niceties, because the implementation was too complex for me, like WebSocket-based hot module replacement, or I simply didn’t need them, like image optimization.

Obviously, I couldn’t use the main selling point of Astro, which is interoperability with different JavaScript component frameworks. Fortunately, my blog isn’t a very big project, so rewriting everything in Go templates wasn’t very hard.

Another thing I didn’t consider when rewriting my blog was security, because my website is static and doesn’t make any connections to the database. However, looking at the html/template package14 of the Go standard library, the code injection protection is very good out of the box, and I don’t see any reason for it to be less secure than Astro implementation. Additionally, having less dependencies results in less code. Less code equals less opportunities for security bugs to show up, and most crucially less effort to update or patch known vulnerabilities. In OWASP Top 1015 having a vulnerable or outdated component16 is the only potential weakness differentiating Astro and Go implementations. Everything else is either the same or independent of the framework.

The last annoyance was the lack of customizability of the footnote section’s heading. I don’t think it was customizable in Astro either, but the default behavior for the footnote section was “Footnotes,” which already satisfied me. On the other hand, the Goldmark library17 did not put any heading, only a horizontal rule to mark the start of the footnotes section. So I needed to reimplement some internal methods to put the heading back in its place.

Conclusion

You are probably familiar with many such rewrite stories, where a project moves away from JavaScript. Fewer dependencies, faster development, better application performance. My story was a bit different. Yes, the number of dependencies decreased significantly, but my biggest goal was to be able to forget about my project for a couple of months and be able to just write an article, without studying a migration guide for half a day. I think I managed to do it, because I’ve started writing this article November 24, 2023, and went back to it on and off for three months. Now it’s February 24, 2024, and I didn’t need to read any migration guide — my blog renderer Just Works™.

Footnotes

  1. Next.js. The React framework of the Web, 2024. [Online source] (accessed Mar 6, 2024). 

  2. When I implemented my blog for the first time with Python and Django, I somehow got an Internal Server Error every time I had a non-ASCII character in any article. I was stupid back then, so I never figured out why. I still don’t know why, so not much changed. 

  3. Astro. The web framework for content-driven websites, 2024. [Online source] (accessed Feb 24, 2024). 

  4. Solid. A declarative, efficient, and flexible JavaScript library for building user interfaces, 2024. [Online source] (accessed Feb 24, 2024). 

  5. Third major release of Astro, Aug 30, 2023. [Online source] (accessed Feb 24, 2024). 

  6. Astro 3 deprecations and breaking changes, 2023. [Online source] (accessed Feb 24, 2024). 

  7. Fourth major release of Astro, Dec 05, 2023. [Online source] (accessed Feb 24, 2024). 

  8. Dependabot. Automated dependency updates built into GitHub, 2024. [Online source] (accessed Mar 6, 2024). 

  9. Renovate. Universal dependency automation tool, 2024. [Online source] (accessed Mar 6, 2024). 

  10. Hugo The world’s fastest framework for building websites. [Online source] (accessed Feb 24, 2024). 

  11. HTMX. High power tools for HTML. [Online source] (accessed Feb 24, 2024). 

  12. HTMX. Essays and memes. [Online source] (accessed Feb 24, 2024). 

  13. Billing for GitHub Actions. GitHub, 2024. [Online source] (accessed Mar 6, 2024). 

  14. html/template package. Data-driven templates for generating HTML output safe against code injection. Go standard library, 2024. [Online source] (accessed Mar 6, 2024). 

  15. OWASP Top 10:2021. Standard awareness document for developers and web application security, 2021. [Online source] (accessed Mar 6, 2024). 

  16. Vulnerable and Outdated Components. OWASP Top 10:2021, 2021. [Online source] (accessed Mar 6, 2024). 

  17. Goldmark. A Markdown parser written in Go, 2024. [Online source] (accessed Feb 24, 2024).