Porting LangGraph to Java in one weekend? (with Claude Code)
🎶 With a little help from my LLM friends
I saw an article on HN Saturday morning about using Claude Code to un-minify its own source code, and thought it would be interesting to spend the weekend trying out a rather more involved task.
I’m the author of LangGraph, (one of) the top LLM agent framework in Python. We ported it to JS by hand a while ago, and it’s an ongoing effort to keep them both in sync. We have heard some interest for a Java version of LangGraph, so I decided to see how far I could get in one weekend, porting LangGraph to Java, just me and my LLM friends.
Before we get started, I want to call out attention to the work needed before you start using your shiny LLM helper. There's a bunch of context you have about your project that Claude doesn't have and won't find from the code. It’s super important that you first reduce the amount of that context that Claude will need, ie. cut down on scope and requirements. And then codify as much of the remaining missing context as possible. More on this below.
The process I followed
This whole thing turned out to be a long process, with more steps than I had originally planned for, so let’s dive in:
Remove things specific to your source language
Me: 95%, LLMs: 5% (GitHub Copilot) — these are my completely unscientific estimates of how much of the work in each step to attribute to me or the LLM.
Your project is likely to contain things that are too specific to the source language, eg. dependencies that don't exist in the other one, stuff to work around quirks in the current language etc. The more of those you remove before starting the better.
Spring clean your source implementation
Me: 95%, LLMs: 5% (GitHub Copilot)
Your project is likely full of stuff you've learnt to ignore (because it's deprecated, superseded, or just plain embarrassing). Your friendly LLM won't know to ignore it, so I suggest removing those things before proceeding.
Ask Claude to write a detailed spec for your project
Me: 50%, LLMs: 50% (Claude Code)
This will be crucial in coming up with an implementation plan, as it will determine the order things should be implemented in. You’ll likely need to provide lots of input here, Claude hallucinated a bunch of nonsense in this section for me, which I had to delete or correct.
Get Claude to write the public interfaces in the target language
Me: 5%, LLMs: 95% (Claude Code)
Armed with the spec, and the original implementation, get Claude to write out the public API you plan to implement. This step is crucial to ensure you end up with an implementation that feels native to the target language – you’ll want to ask Claude to follow the patterns and idioms of that language. This is an almost perfect task for the sometimes annoying "middle of the road" attitude of LLMs. It will also help a bunch later on with following TDD if Claude knows the public APIs it should be writing tests for.
Ask Claude for a roadmap
Me: 5%, LLMs: 95% (Claude Code)
This will guide the next step, and will basically be the result of combining the outputs of the two previous steps. Here Claude actually produced a pretty solid plan, which I didn’t have to edit. Obviously the wild time estimates are nonsense, but they serve no purpose anyway.
Ask Claude to start coding
Me: 20%, LLMs: 80% (Claude Code)
Now the next steps, 6 through 8, will really be an iterative loop. This is where you ask Claude to get cracking, and start acting on the plan. It will need a lot of prodding along the way to keep going. You should treat what you get here as you would a draft PR from a very junior engineer, it will have mistakes large and small, which will take meaningful time to find and fix.
Your turn to code review
Me: 50%, LLMs: 50% (Claude Code) — these 50% attributed to Claude here is for fixing the issues found, not finding them.
All your precious code review skills come in very handy here, Claude will write so much more code than a person would in the same period of time. My process here was roughly to 1. Ask for tests and TDD repeatedly, 2. Ask Claude to create and keep updating a mapping between original and new implementation, and ask it to consult it each time, 3. Identify issues, update CLAUDE.md, ask to fix, 4. Rinse and repeat.
Get some fresh (LLM) eyes
Me: 20%, LLMs: 80% (o1 pro)
I found it very helpful to get another model (in my case o1 pro) involved from time to time, whenever Claude would tell me some issue I found couldn’t be fixed, I got o1 pro (with some back and forth) to come up with a solution, fed it back to Claude, and promptly got the usual “ah yes you’re absolutely right”.
Let’s look in more detail at what I went through to fix a single issue.
A deep dive into fixing a single issue
I identified the issue when reviewing the code, and ask for a plan to fix it, in this case for over-usage of @SuppressWarnings("unchecked")
.
And Claude powers through it
Sometimes it needs a little encouragement
And other times it needs some guidance, my prompt below
- how can we make the Pregel class fully type-safe
- the input to the graph should be typed from the Update types of the input channels
- the input to each PregelNode should be typed from the Value types of each of the “read from” channels
- the output of each PregelNode should be typed from the Update types of each of the “write to” channels
- the output of the graph should be typed from the Value types of the output channels
And sometimes it forgets to follow instructions. I had previously updated CLAUDE.md with specific instructions to not leave the old implementation around when refactoring.
(Partial) success!
Ah but these code snippets don’t look so good, let’s see what o1 pro thinks…
Pregel<String, Integer> pregel = new Pregel.Builder<>(String.class, Integer.class)
.addNode(node)
.addChannels(channels)
.build();
Let’s get Claude on it!
Ah yes, implementing a type-safe API and then typing everything as any, just as any good junior software engineer would do :)
Just you wait, just you wait, I’m more stubborn than you
And our friend o1 pro delivers again, after a little back and forth
Ah yes quite excellent thank you very much!
Many tokens later…
In the end, I didn’t quote a fully-featured Java port of LangGraph in one weekend, but I got something! It’s functional and passes tests, in my book that’s a big win already.