One of my favorite phrases in software development is “Round-Trip Gestalt Design.” I think it came from Grady Booch.
The idea is that design (done well) involves diving deep into the details to make changes and then stepping back to see things from a higher level — possibly making changes there.
It makes sense. The things we change at the lower levels of our system often lead to new understanding. We figure out what a system is when we work on it and then we reflect that understanding back into the design.
Sometimes this involves changing the larger parts of the system – renaming them and altering the way that they relate to each other.
The only problem with this approach is that sometimes the pieces are fuzzy – not discernible. This is common in older systems. Code grows organically and responsibilities merge – they are no longer crisp enough to notice separately.
When talk about LLMs started to ripple through my friend group a few years ago. I mentioned that I’d love to have a tool that allows you to TL;DR articles at different levels of detail. It would be like dynamically increasing and decreasing font size in an editor or a Kindle. You adjust the size down when your vision is sharp and up when you are fatigued.
I’d love to be able to do this with detailed writing. It would be great to be able to pull back and read a single page condensation of a 10 page article and then zoom in to see the concepts in 3 pages. Yes, you can do that today with prompts in LLM chat interfaces but having a tool to do just that would be great.
It would also be great to have that for code.
One of my favorite teaching examples is this email server code of dubious quality.
It’s one page.
No, it’s worse than that. It’s just one function and nearly every line is a library call.
Think about how you would refactor it.
Here’s how I approached it (with AI help):
Me: What would be a good method extraction to start with in the Server class?
Response: The Server class has a main method that is quite long and does a lot of different things. It would be beneficial to break down this method into smaller, more manageable methods. One good place to start would be the section of code that reads the email list file and populates the toList array. This is a distinct operation that could be encapsulated in its own method. Here's a suggestion for a new method called readEmailListFile: <...>
This is a nice response. Now, I’m now curious about other extraction candidates.
Me: Name the 7 most important responsibilities in Server.
Response: The Server class in your Server.java file has several responsibilities. Here are the seven most important ones:
Email List Reading: The Server class reads an email list from a file and stores it in a Vector. This is done in the main method.
Email Session Creation: The Server class creates a Session object using system properties. This is also done in the main method.
Email Store Connection: The Server class connects to an email store using the Session object. This is done in the main method.
Email Folder Access: The Server class accesses the default folder and the inbox folder from the email store. This is done in the main method.
Email Retrieval: The Server class retrieves all messages from the inbox folder. This is done in the main method.
Email Processing: The Server class processes each email message, checks if it's unseen, and then sends it to all email IDs in the list. This is done in the main method.
Email Sending: The Server class sends the processed email message using a Transport object. This is done in the main method.
This looks good but it isn’t quite what I want. Many of these are implementation related – not the high level responsibilities I’m looking for. I’ll try to narrow it.
Me: Name the 4 most important.
Response:
Email List Reading: The Server class reads an email list from a file and stores it in a Vector. This is done in the main method.
Email Session Creation: The Server class creates a Session object using system properties. This is also done in the main method.
Email Retrieval: The Server class retrieves all messages from the inbox folder. This is done in the main method.
Email Processing and Sending: The Server class processes each email message, checks if it's unseen, and then sends it to all email IDs in the list. This is done in the main method.
Hmm… it still thinks Session creation is very important.
Me: Name the 3 most important from the point of view of a client.
Response:
Email Retrieval: The Server class retrieves all messages from the inbox folder. This is important for a client as they would want to ensure all their messages are being retrieved correctly.
Email Processing: The Server class processes each email message, checks if it's unseen, and then sends it to all email IDs in the list. This is crucial for a client as they would want their messages to be processed accurately and efficiently.
Email Sending: The Server class sends the processed email message using a Transport object. This is vital for a client as they would want their messages to be sent out correctly to the intended recipients.
Me: What about scheduling? Is there any scheduling code?
Response: Yes, there is scheduling code in the Server class. The scheduling is done using a while (true) loop that runs indefinitely. Inside this loop, the email processing is performed and then the thread is put to sleep for a certain period of time before the next iteration. This period is defined by the checkPeriod variable, which is set from the command line arguments.
Nice. It was able to find that one.
Me: Add that as a responsibility.
I’ll spare you the details, but I was able to interactively hone a view of the system through several more prompts.
I like to lens back and forth this way (yes, I am using lens as a transitive verb) to help me set my direction, or just understand.
With the state of some tools today, this is a frustrating process. On the other hand, the degree to which many of them can identify responsibilities is surprising. I’m actually finding that the major generic chat tools are better than those embedded in developer tools right now — no doubt because they have more resources. I think that will change as the space specializes.
Regardless, lensing is practicable now. It is a way that we can leverage AI without the risks associated with incorporating generated code in our systems. It’s only going to get better.
I am immediately comparing your experience asking an LLM to identify responsibilities with what would happen if I tried to summarize the code using the Composed Method pattern. I'm also aware of my propensity to side with my established habits. :)
I'm left concluding this:
- The LLM helps me more when what I need is the summary.
- Extracting to Composed Method helps me more when what I need is a richer understanding of the code, which includes a helpful summary.
I genuinely don't know when I want the summary without the richer understanding. I have a bias towards the opinion that the act of extracting is much more valuable than having someone else's summary of the code.
And as I type these words, I realize one time when I only want the summary: when I intend to fix just one little thing in the code and I merely want to do it safely. In that situation, I don't need to understand the code; I merely need to change it without burning my fingers.
Huh.