
Announcing The New Foojay
Foojay has moved, but the address stays exactly where it was, just like every URL you have bookmarked, linked or cited. Everything behind that address is new. Foojay is now a static site, its entire content lives in a public Git repository, and publishing an article is a pull request.
If you only read one paragraph: nothing you have written has moved, and nothing you need to do has changed, except if you want to contribute content.
Why This Change?
Foojay was a WordPress site, and it did the job for six years and 2,000+ articles. But a few things had been quietly getting in the way.
Publishing had friction that had nothing to do with writing. An author with something good to say had to get an account, learn an editor, and hope the formatting survived. For instance, Markdown was available, but code blocks sometimes looked messy after publishing. Meanwhile most of our authors already work in an IDE or text editor, and a Git client all day.
As the amount of posts grew, WordPress became slower. But not only page load time increased. The whole process to add new features, improve the styling, fix bugs, constantly update plugins and WordPress itself, it all started taking too much time, effort, and money for a community driven project.
AI happened. WordPress was one of the few options to build something like Foojay for a long time. But with Hugo and AI assisted coding, replacing what we had into something faster and more user-friendly for visitors, authors, and maintainers, it became possible to build what you see now in a matter of weeks with a very small budget.
One evening I was “fed up” with recurring issues and decided to see if I could do better. I had a few ideas, and I wanted to see if they were feasible. I started a small experiment, and that night at 03:00 I was convinced I could make this work and port all existing content to a Hugo website, with extra features, and a full cleanup.
To put real numbers on “a matter of weeks with a very small budget” for the version of Foojay you are now looking at, after a lot of rounds of testing, refactoring, improving, and importing content from WordPress to GitHub:
- About 100 hours of work across 2 months of which a lot outside official working hours as I got dragged into a mission ;-)
- Roughly $1000 of AI model usage at published token prices. This number was given to me by Claude itself, so no idea how accurate this is, and what the real cost is to the company that pays the subscription…
The New Foojay Generated by Hugo and Maintained by Java
Foojay.io is now a Hugo site built from a public repository, deployed automatically with GitHub Actions to GitHub Pages whenever something lands on the main branch. Concretely:
| Before | Now | |
|---|---|---|
| Publishing | CMS account + web editor | Pull request |
| Search | Server-side | Static index, in your browser |
| Comments | Plugin | GitHub Discussions, with the old ones kept as an archive |
| Read counts | Plugin | a counter we run, that stores a slug and a number on Cloudflare |
| Analytics | Google Analytics via 360 KB of Tag Manager | The same Google Analytics, loaded directly |
| Podcast episodes | Press play and listen | Idem plus a transcript on the page as well |
| All content | In a database | Markdown files you can read on GitHub |
Everything came across: 2250+ articles, 350+ author profiles, 50+ JVM Terms Explained, the JUG (Java User Group) directory, the calendar, the sponsors,… Every legacy path
still resolves, including the ones nobody would think to check: the old /blog/
scheme, the retired slugs, and the URLs that changed when an article got a new
name years ago.
Hugo? No Java?
Foojay.io is a website for the Friends of OpenJDK, so you would expect Java to be used here. And it is, but not for the publishing part.
I did look at the Java generators. I had tried JBake before, and at the time it looked like a project that had gone quiet. No release between early 2023 and late 2025 is a long gap to stake a site on. (Credit where it is due: it has since shipped 2.7.0.) And when I mentioned this project on the Foojay Slack, someone pointed me at Roq, the Quarkus-based static site generator. It is a thin layer over Quarkus that renders Markdown through Qute templates, with type-safe templating and code completion. It looks genuinely good, and if you want your generator in Java too, start there.
I went with what I already know. I have built a lot of very different sites on Hugo: webtechie.be, codewriter.be, pi4j.com, lottie4j.com and melodymatrix.rocks, vikdelportemusic.be. Personal blogs, open-source project’s documentation, product sites,… All of them work well, and Hugo has been stable, long-lived and very actively maintained the whole time. For a community project that has to still be standing in ten years, “I know this tool and it is not going anywhere” beat “this is the most interesting choice”. It also builds all articles, the complete site, and the search index in a few minutes on GitHub Actions.
And the generator is the smallest part of this anyway. There are a lot of different Java scripts for the migration, the daily data syncs,
the checks on your pull request, etc. That is the part that actually made this
possible. All the scripts are in the repository in the scripts/ directory.
All of it runs on JBang, so there is no pom.xml, no
Gradle, no build step. Each script is a single file that declares its own
dependencies at the top and runs directly:
///usr/bin/env jbang "$0" "$@" ; exit $? //DEPS org.jsoup:jsoup:1.17.2 //DEPS com.vladsch.flexmark:flexmark-html2md-converter:0.64.8 //JAVA 21+
As an example: jbang scripts/transfer/Posts.java read all the content from the WordPress site, uses jsoup to parse the HTML, flexmark turns it into Markdown, Jackson and SnakeYAML handle the JSON and YAML. As a Java developer, those are the tools I know and use in other projects. Based on this info, Claude generated the scripts, I reviewed, and used it over and over again until the cutover to keep the old site live while the new one was being built and tested.
During that process, the content got cleaned up to become a proper Markdown site. A simpler image gallery got added, code blocks became fenced blocks, links got checked and fixed, and the frontmatter at the top of each article now asks for six things: title, date, description, author, hero image and categories. Everything else is derived from that.
One potential problem: GitHub Pages has a limit of 1Gb. A lot of images were imported from the old site, and some of them were huge. The largest one was a 52 MB animated GIF that was used as the header image of three different articles. That alone was enough to push the repository over the limit. So a script got added to reduce file sizes and we are now below the limit with some room for the future. We may need to move images to a CDN later, but that’s not an urgent thing at this moment.
I’ll wait a few days until to see if any issues arise, and then I’ll remove some of the script which are only needed for the migration. What will stay, are the Java scripts that keep the site up-to-date:
| Runs | What it does |
|---|---|
| On every deploy + daily | Pulls the JUG directory and the Java Champions list from the community-run repositories that own that data |
| Daily | Reads the iCal feed each JUG publishes, so meetups appear on the calendar with nobody typing them in |
| 4× a day | Refreshes the read counts |
| On every pull request | Validates frontmatter, the check that tells you about, e.g., a mistyped author slug before a human looks at your article |
What’s Important for Authors
Your article is now a folder, to be structured like this:
draft/
my-great-article/
index.md or .adoc <- the article, in Markdown or AsciiDoc
a-diagram.png <- one or more images used in the articleThe folder name becomes the URL. The images sit beside the text, so they never
get lost. The frontmatter at the top of index.md or index.adoc needs the title, description, author, hero image and categories. There is no “SEO section” to fill in, no excerpt to write twice, no tag taxonomy to guess at.
Open a pull request and an automated check reads your frontmatter and builds the whole site. If you mistyped your author slug or forgot to commit an image, you find out in about a minute, from a robot, before a human has spent any time on it.
Already published the article on your own blog? Use a canonical link and search engines will keep crediting your site while the article reaches Foojay’s readers. Roughly 800 articles here are cross-posts, by using this extra line in the frontmatter section:
canonical: "https://your-blog.example/the-original/"
All info about writing and submitting an article is on the How To Submit Your Next Article page.
Automated Checks
Automated checks run before anyone from the Foojay team reads your article, and each one catches a different kind of mistake.
On the pull request. Besides your frontmatter, the check builds all pages, so a broken shortcode shows up as a red cross on your PR instead of a broken page on the site.
On the built site, before it deploys. Did every source file produce a page? A broken section rarely throws an error. It renders a template that runs fine and matches nothing. The check also resolves every internal link against the files on disk, half a million of them in about five seconds.
In a real browser. Search, the two world maps, the image lightbox, the sortable tables in the sitemap, syntax highlighting,…: none of it exists until JavaScript runs, and all of it fails the same quiet way. The page returns 200, looks complete, and stops doing its job. So about forty checks click through the finished site.
One check covers security rather than mistakes. Foojay’s Markdown allows raw
HTML, and it has to, because two thousand imported WordPress articles carry
tables and embeds with no Markdown equivalent. A merged pull request could
therefore carry a <script>, and a static site keeps no server-side layer to
catch it. So the check refuses executable markup in article text. It skips
fenced code blocks, where Hugo escapes everything anyway, so you can still
write about XSS as long as you fence your examples.
Podcasts are Now Readable
The Foojay Podcast had exactly one way in: press play and listen for the next
hour. But now in this new site, 100 episodes have a transcript on the page, roughly 824,000 words of conversation you can read, skim, Ctrl-F through, or quote from.
Nobody transcribed anything to make that happen. Every episode sits on YouTube, where speech recognition already has fetched the text. Seconds per episode, against hours of local compute for a result of the same quality. A JBang script pulls the captions and writes a
transcript.md next to the episode’s index.md. The page renders it because
the file is there, so there is no flag on the episode to set and none to
remember to unset. It is a plain section of the page with its own entry in “On
this page”.
But, don’t forget, it is a machine transcript, which every episode says out loud above the
text. Automatic captions get names and Java vocabulary wrong. The script tries to fix some recurring mistakes, like rewriting fujy to Foojay. On the other hand, it deliberately leaves guests’ names alone. Recognition mangles them
worse than anything else, but no script can know which spelling someone
intended, and inventing one puts words in their mouth. So if you were on an
episode and your name comes out wrong, every transcript carries a Suggest a
correction link that opens that episode’s transcript file in an editor. The
script never overwrites a corrected transcript again.
One deliberate omission: transcripts stay out of the search index. as they would add to much “noise” to the search results. The search index is for articles, not for every word spoken in a podcast.
JUGs, Calendar and Events
One of the most important things Foojay does is connect Java developers to each other. The JUG directory and the calendar of meetups are fully automated, and anyone can add a conference or event with a pull request.
Automated Calendar of JUG Meetups
The Foojay WordPress had a system to import Meetup events from the JUGs it knew about, but it was limited to that list and JUGs had to let us know that they were using Meetup. This has now been changed to a fully automated system in two steps:
- The list of Java User Groups is not ours to maintain as there is already a public source! It comes from GlobalWWJugs, the community-run directory that already tracks which JUGs exist and where, and a JBang script pulls it into the site at every deploy. A JUG lead who wants to correct their entry opens a pull request against that repository, not the Foojay one.
- The Foojay calendar is now fully automated. It reads the iCal feed each JUG publishes, and it does not care whether that feed comes from Meetup, Google Calendar, a file on the group’s own site, or any other platform that exports iCal. Once a day a second script walks that list and reads the calendar feed to fill our calendar data file.
102 JUGs sit in the directory and 70 of them publish a feed we can read, and 62 upcoming meetups sit on the calendar as I write this.
Both screenshots below show September 2026, taken on the first of the month:


Conferences and Events
Conferences and other events work differently. Nobody subscribes to Devoxx in their calendar app the way they subscribe to a
JUG, so those entries live in the repository as one small YAML file each, and
anyone can add one with a pull request. Copy template/event.yaml, fill in the
name, the URL and the dates, and open a PR. The frontmatter check reads it at
review time and fails on a key it does not recognise. One file per event also means two people adding two conferences in the same week never touch the same data, so they never collide.
Nothing has to be deleted afterwards either. The page drops an event the day after it ends.
Both routes are on the calendar page itself. Add an event opens the events folder in this repository. Add your JUG opens the repository upstream.
Comments Moved to GitHub Discussions
Foojay collected 580 comments across 270 articles over six years, and a lot of them earn their place.
New comments are handled with giscus, which keeps each thread as a GitHub Discussion on the same public repository as the articles. You sign in with GitHub, the thread has a URL you can link to, and the team moderates it with the tools it already uses every day.
The 580 old comments needed a different answer, and the first attempt failed… The plan was to post them into those same Discussions from the Foojay account, so that an old conversation and a new one looked identical. GitHub blocked the transfer after a few articles because it looked like a spam bot…
So the old comments now live in the repository instead. Each article carries a small JSON file next to its text, and the page renders it under the live discussion as Discussions on the previous Foojay site.
Accessibility
No law seems to oblige Foojay to be accessible. The European Accessibility Act covers consumer services in listed sectors, not community blogs. That is a poor reason to skip it. Java developers are precisely the audience that browses with a keyboard, at 200% zoom, in a dark colour scheme, or with a screen reader.
So the site targets WCAG 2.2 AA, and there is now an accessibility statement that says where it actually stands. What went in:
- A skip link, one
<h1>and real landmarks on every page. - Everything should work without a mouse: the menu, the search field, the image viewer, the event calendar, the sortable tables, the sponsor banners. Read the accessibility page for more information about navigating with the keyboard through the site.
- We measure contrast, and the stylesheet records the number next to each colour. That is how we found that Foojay’s own logo blue is 2.02:1 on white. Fine as a fill, unusable as text. So it is never text here, and the focus indicator got a colour of its own. Links in running text carry an underline for the same reason: at 1.2:1 against the body text, colour alone told you nothing.
- Nothing moves that you cannot stop, and nothing moves at all if your system asks for reduced motion.
- The mobile menu moved off-screen but stayed in the tab order, so a keyboard walked you through a menu you could not see.
- The image viewer bound its click handler to the
<img>, so click-to-enlarge did not exist for a keyboard at all.
One thing which could not be easily fixed: roughly 3,000 images in the archive have no alt text. They arrived that way, and no
script can or should invent a description. The check reads
new articles when you open the pull request and raises a warning rather than a
failure, because whether an image carries meaning is a judgement call. The
predictable response to a hard failure is alt="image", which serves a screen
reader worse than nothing at all. If you
wrote one of those articles on WordPress, you are the best person alive to describe its screenshots.
Visitor Counting and Privacy
There are two counters, for different reasons. One is Google Analytics, which the marketing team uses to understand traffic patterns. The other is a counter we run ourselves, which counts how many people actually read an article.
1. Google Analytics, because marketing loves it
Foojay reports into the same Google Analytics property it always has. That has not changed, and we are not going to pretend otherwise. Ketch is still the consent manager, and Google Consent Mode now defaults every category to denied before it loads. So until you actually agree to something, GA sets no cookie and sends cookieless pings. If you decline, it stays that way.
You use an ad blocker and refuse cookies? No problem, so do I. That is exactly why a second counter exists.
2. Read counter, because we want to know what people actually read
With Google Analytics as the only source, every number we publish comes out wrong, and wrong in a predictable direction. A large share of Java developers and Foojay visitors use ad blockers. A page-view count that silently misses a a big part of its readers is not a statistic, it is a guess. And the read count on a Foojay article is a published number, sitting on the page next to the byline, so it had better be true.
So the 12,345 views you see comes from something we run ourselves: a small
Cloudflare Worker on
https://foojay.io/api/views/all, in front of a table with exactly two columns. A page key
like posts/announcing-the-new-foojay, and an integer. No visitor records, no IP addresses, no sessions, no personal data of any kind.
Nobody’s article went back to zero. We transferred the numbers from WordPress to keep the history: 13.8 million reads over 2,147 articles, 47 glossary entries and 32 pages. Each one became the starting value of that page’s count.
That is the whole design, and the privacy properties fall out of it rather than sitting on top of it as a promise:
- No cookie, no identifier, no IP address, no user agent, no fingerprint. The request carries a page key and nothing else. There is nothing to anonymise because nothing about you is ever sent.
- It cannot follow you between pages, because it stores no notion of a visitor. Two reads of two articles are two numbers going up, with nothing linking them.
- An ad blocker does not need to block it, because there is nothing to block. It is first-party, on the same domain as the article you are reading. That is not a loophole. It is why the number is accurate.
- The build bakes the count into the page, so displaying it costs no request at all. No JavaScript and no placeholder that turns into a number a second later.
The single piece of state on your machine is a sessionStorage flag that stops
a page refresh counting twice, and it dies when you close the tab.
And the thing doing the counting is in this repository too: the Worker is under two hundred lines, comments included, and you can read every query it makes against that table.
For the formal version, see the privacy policy.
Come and Write
One thing about Foojay never changes: Foojay is worth reading because people in this community take the time to write things down.
If you have ever thought someone should write that up, that someone can be you. A JVM flag that saved you a week, a migration that went sideways, a library nobody knows about, a conference report, a thing you finally understood. You do not need to be a Java Champion, and it does not need to be 3,000 words.
- Start here: How To Submit Your Next Article On Foojay.io
- Or just read the templates: the
template/folder holds everything you need. Copy a file, fill it in, open a pull request. - Questions, or not sure your idea fits? Ask in the Foojay Slack. The answer is usually yes.
If you spot a typo in this or any other article or page, scroll to the foot of it and look for the Edit this page on GitHub link. It opens the exact file this page came from, in an editor, and turns your fix into a pull request. GitHub forks the repository for you, so it takes three clicks and no local setup. Every article, page and glossary entry on the site carries that link.
We hope you enjoy the new Foojay, and we look forward to your contributions. Found an issue or something that could be improved? Open a pull request, or file an issue!
Found a mistake, or something to add? Edit this page on GitHub
Frank Delporte


